抽象类与接口

前言

抽象类与接口在Java开发中占据了十分重要的地位,统一了Java的编码规范.本文总结了抽象类,接口是什么以及他们的区别.对模板设计模式,工厂设计模式,代理设计模式进行了初步的了解

一.抽象类

抽象类的使用形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class TestAbstract {

public static void main(String[] args) {
Persion per = new Student(); //实例化子类,向上转型
per.print();
}
}

abstract class Persion{
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public abstract void print();
}

class Student extends Persion{
@Override
public void print() {
System.out.println("Student中的print方法");
}
}

class Worker extends Persion{
@Override
public void print() {
System.out.println("Worker中的print方法");
}
}

抽象类:由abstract修饰的类被称作抽象类! 与普通的类相比,抽象类中除了普通的方法和属性之外还具有抽象方法.抽象方法就是被abstract修饰的方法,抽象方法只有方法定义没有方法体! 抽象类必须被一个子类实现并且覆写抽象类中的抽象方法.
抽象类很好的体现了面向对象的多态特性,可以有多个类共同实现一个抽象类,抽象类的对象可以通过对象多态性利用子类为其实例化
注意:抽象类中的抽象方法不能用final修饰,因为抽象方法是要被子类拿去覆写的;抽象方法也不能与private一起使用,因为被private修饰的方法,子类是看不到的

1.方法内部类实现抽象类对象的实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class TestAbstract {

public static void main(String[] args) {
Persion per = Persion.getPersionSon();
per.print();
}
}

abstract class Persion{
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public abstract void print();

public static Persion getPersionSon(){
class Teacher extends Persion{
@Override
public void print() {
System.out.println("Teacher的print");
}
}
return new Teacher();
}

}

一般不采用这种方法,但是这种方法的封装性显然很好

2.一道神奇的题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
abstract class A{
public A(){ //3.调用父类构造
this.print() ; //4.调用被子类覆写的方法
}
public abstract void print() ;
}
class B extends A{
private int num = 100 ;
public B(int num) { //2.调用子类实例化对象
super() ; //3.隐含一行语句,实际要先调用父类构造
this.num = num ; //7.为类中属性初始化
System.out.println(this.num) //此时已经初始化好,打印为30
}
public void print() { //5.此时子类对象的属性还没有被初始化
System.out.println(this.num) ; //6.对应其数据类型的默认值
}
}

public class Test{
public static void main(String[] args) {
new B(30) ; //1.实例化子类对象
}
}

分析: 当创建B实例化对象的时候会首先到B的构造方法中去,但是抽象类也和普通类一样,他遵循普通类的构造规则,总是先去调用父类的构造方法,所以在他走到B类的构造器时的第3步他会去调用父类的构造器(如果父类没有默认构造方法,则这里要明确指定调用父类的哪一个构造方法),父类构造方法中又调用了子类所覆写的方法,于是又跑到了子类中,而此时num值还并没有初始化,因为一直都没有走到第7行…(B类构造器传进来的num只是一个形式参数,在没有赋值前他并没有实际意义),于是在子类覆写的print方法中打印的是还没有初始化的num的值0,然后回到第7步,此时num才为30; 而100一直都是没有用到的!

3.关于对象实例化的补充

对象实例化大致分为三步

  • 进行类加载
  • 进行类对象的空间开辟(new …)
  • 进行类对象中的属性初始化(构造方法)

4.抽象类的相关规定

  • 抽象类就是被abstract修饰的类,抽象类比普通类多出了抽象方法
  • 抽象类中可以没有抽象方法,但他仍然是抽象类,不能够直接创建实例化对象
  • 抽象类不能用final修饰,因为final修饰的类是不能够被继承的,抽象类需要被子类继承,通过子类的实例化向上转型创建自己的实例化对象
  • 抽象方法不能用private修饰,因为抽象方法需要被子类来覆写
  • 抽象类也分为内部抽象类和外部抽象类.内部抽象类可以使用static修饰来描述外部抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class AbstractInner {
public static void main(String[] args) {

}
}

abstract class AA{

abstract public void print();

//内部抽象类
abstract class BB{
abstract public void say();
}
}

class XX extends AA{

@Override
public void print() { }

class YY extends BB{
@Override
public void say() { }
}
}

内部抽象类可以使用static修饰,外部抽象类不可以!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class AbstractInner {
public static void main(String[] args) {

}
}

abstract class AA{

abstract public void print();

abstract static class BB{
abstract public void say();
}
}

//内部抽象类使用static修饰了,所以可以直接继承内部抽象类,写法如下
class x extends AA.BB{

@Override
public void say() {}
}

抽象类与一般类的最大的区别就是规定了子类的实现要求,但是抽象类依然是存在单继承的缺陷的…

二.模板设计模式

模板设计模式是抽象类的一个实际应用场景
Java中设计模式都遵循一个OCP原则(开闭原则): 一个软件实体,如类,模块和函数应该对扩展开放,对修改关闭
先来举一个小的例子,假设在星巴克中冲泡两种不同的饮品方法如下:
星巴克咖啡冲泡法

  1. 将水煮沸
  2. 用沸水冲泡咖啡
  3. 将咖啡倒进杯子
  4. 加糖和牛奶

星巴克茶水冲泡法

  1. 将水煮沸
  2. 用沸水浸泡茶叶
  3. 把茶倒进杯子
  4. 加柠檬

下面我们不是用模板模式写出两中炮制饮品的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class ModelDesign {
public static void main(String[] args) {
System.out.println("------制作coffee--------");
Tea tea = new Tea();
tea.teaMethod();

System.out.println("---------制作茶水---------");
Coffee coffee = new Coffee();
coffee.coffeeMethod();
}
}

class Tea{
public void teaMethod(){
boilWater();
boilWithTea();
teaInCup();
teaWithLemon();
}

private void teaWithLemon() {
System.out.println("4.茶里加入柠檬");
}

private void teaInCup() {
System.out.println("3.把茶倒进杯子");
}

private void boilWithTea() {
System.out.println("2.用沸水煮茶");
}

private void boilWater() {
System.out.println("1.将水煮沸");
}
}


class Coffee{

public void coffeeMethod(){
boilWater() ;
WaterWithCoffee();
CoffeeInCup();
SugerAndMilk();
}

private void SugerAndMilk() {
System.out.println("4.给咖啡里加糖和牛奶");
}

private void CoffeeInCup() {
System.out.println("3.咖啡倒进杯子里");
}

private void WaterWithCoffee() {
System.out.println("2.沸水冲咖啡");
}

private void boilWater() {
System.out.println("1.烧水");
}

}

  • 我们可以看到其实制作咖啡和制作茶水是有很多相同点的.所以这样的代码写出来重复度是十分高的. 模板模式就为我们解决了这种情况下的代码冗余重复问题
  • 模板模式: 将共同的部分抽取出来,放进一个基类中(抽象类中的普通方法),只要是继承这个基类的类都能够具有此方法,然后将有区别的方法找出来设置为抽象方法,等待继承的子类自己去实现
  • 具体整理下:
    1.烧水 –> 相同
    2.沸水煮茶/咖啡 –> 不同
    3.倒入杯子 –> 相同
    4.茶加柠檬/咖啡加牛奶和糖

  • 例如上述情况,我们可以看到其实冲泡两种饮料中的第一步和第三部都是相同的,都是烧水,导入杯子中,所以这两个相同方法就可以写入到抽象类的普通方法中,这样只要实现这个抽象类的实现类就都有这两个方法,然后两种饮料的冲泡方法和加入的配料不一样,我们就将这两个方法设为抽象方法,等待着子类自己去实现

  • 代码演示
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    public class TemplateDesignTest {
    public static void main(String[] args) {
    BrewDrinks brewDrinks1 = new Teain();
    brewDrinks1.prepareRecipe();
    System.out.println("------------------");
    BrewDrinks brewDrinks2 = new Coffeein();
    brewDrinks2.prepareRecipe();
    }

    }

    abstract class BrewDrinks{

    //模板方法是用final修饰,因为我们不希望子类修改模板方法
    public final void prepareRecipe(){
    boilWater();
    brewMethod();
    InCup();
    AddMaterials();
    }

    protected abstract void AddMaterials();

    private void InCup() {
    System.out.println("3.将饮品倒入杯子");
    }

    protected abstract void brewMethod();

    public void boilWater(){
    System.out.println("1.将水烧沸");
    }
    }


    class Coffeein extends BrewDrinks{
    @Override
    protected void AddMaterials() {
    System.out.println("4.加入牛奶");
    }

    @Override
    protected void brewMethod() {
    System.out.println("2.沸水冲泡咖啡");
    }
    }

    class Teain extends BrewDrinks{

    @Override
    protected void AddMaterials() {
    System.out.println("4.加入柠檬");
    }

    @Override
    protected void brewMethod() {
    System.out.println("2.沸水煮茶");
    }
    }

以上就是改进后的模板设计模式,我们可以看到此时这个抽象类就像是一个模板一样,她有自己的一套方法,他的每一个实现类都能够按照这个模板去进行自己的工作,相同的方法他拿去继承直接使用,不同的子类可以自己实现,这就是模板设计模式
总结,模板设计模式: 在一个抽象类的普通方法中定义好一个算法的骨架,此中包含抽象方法和普通方法,普通方法就是每个子类都需要使用的公共方法,抽象方法是子类需要延迟实现的方法.模板设计模式可以使得子类在不改变算法结构的情况下来重新定义算法中的某些步骤:
模板模式的优点:

  • 由超类主导一切,超类拥有算法,并且保护这个算法
  • 有超类的存在,实现了代码的复用,降低了代码的冗余度
  • 算法只存在于一处,便于修改
  • 弹性高,新加进来一种饮料我们也可以使用该模板
  • 超类只需要专注于算法本身,而由子类提供完整的实现

钩子方法 :
钩子方法是一类”默认不做事的方法”子类可以视情况决定要不要覆盖它们。例如此例中我们可以根据客人自己需求来决定是否要加糖,这时钩子方法就起到了作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public class TemplateDesignTest {
public static void main(String[] args) {
BrewDrinks brewDrinks1 = new Teain();
((Teain)brewDrinks1).setSuger(false);//自定义决定是否加糖
brewDrinks1.prepareRecipe();

System.out.println("------------------");
BrewDrinks brewDrinks2 = new Coffeein();
brewDrinks2.prepareRecipe();
}

}

abstract class BrewDrinks{

public final void prepareRecipe(){
boilWater();
brewMethod();
InCup();
AddMaterials();
if(isAddSuger()){
this.addSuger();
}
}

abstract void addSuger();
abstract void AddMaterials();
abstract void brewMethod();

//钩子方法,超类中默认实现,子类需要去自己覆写钩子方法,来自定义接下来要做到事
public boolean isAddSuger() {
return true;
}

private void InCup() {
System.out.println("3.将饮品倒入杯子");
}

private void boilWater(){
System.out.println("1.将水烧沸");
}

}


class Coffeein extends BrewDrinks{

//子类覆写钩子方法,实现方法的自定义
public boolean isAddSuger(){
return false;
}

@Override
protected void addSuger() {
System.out.println("5.咖啡中加糖");
}

@Override
protected void AddMaterials() {
System.out.println("4.加入牛奶");
}

@Override
protected void brewMethod() {
System.out.println("2.沸水冲泡咖啡");
}
}

class Teain extends BrewDrinks{

private boolean suger = false ;

public void setSuger(boolean suger) {
this.suger = suger;
}

public boolean isAddSuger(){
return this.suger;
}
//上面几行都是钩子方法的具体具体自定义实现


@Override
protected void addSuger() {
System.out.println("5.给茶中加糖");
}

@Override
protected void AddMaterials() {
System.out.println("4.加入柠檬");
}

@Override
protected void brewMethod() {
System.out.println("2.沸水煮茶");
}
}

以上便是模板设计模式,以后会学习Servlet,模板设计模式是他的基础

三.接口

1.接口的基本定义

接口是抽象方法和全局常量的一个集合,在java中接口使用interface关键字来定义
子类必须使用关键字implements来实现接口,子类必须实现接口中的抽象方法,一个子类可以继承多个接口
接口基本使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class InterfaceTest {

public static void main(String[] args){
//向上转型接口的实例化对象
Message message = new MessageGet();
message.print();

News news = (News)message;
System .out.println(news.getNews());
}
}



interface Message{
public static String msg = "接口中的字符串";
public abstract void print();
}

interface News{
String getNews();
}

class MessageGet implements Message,News{

@Override
public void print() {
System.out.println(Message.msg);
}

@Override
public String getNews() {
//接口中的全局常量也可以通过接口名直接调用
return Message.msg;
}
}

2.接口的使用限制


接口中的方法和属性都要用public来修饰,即使没有修饰默认的也是public权限,在编程的过程中,一般接口中都不提供全局变量,基本都是抽象方法的集合
阿里公约规定
① 在接口中不添加修饰方法或者属性的关键字(保证简洁性)

当一个子类即需要继承一个抽象类又需要实现接口的时候,应该先使用extends继承一个类再使用implements来实现接口

一个抽象类可以实现多个接口,但是接口不能继承抽象类

一个接口可以使用extends来继承多个父接口

接口可以定义一系列的内部结构,包括:内部普通类,内部普通类,内部接口。其中,使用static定义的内部
接口就相当于一个外部接口

1
2
3
4
5
6
7
8
9
10
11
12
13
class CC implements B.C{
@Override
public void printC() {
System.out.println("cc");
}
}

interface B{
void printB();
static interface C{
void printC();
}
}

3.接口的应用

接口在实际开发中的三大应用

  1. 定义操作标准
  2. 表示能力
  3. 在分布式开发之中暴露远程服务方法
    应用: 现在要求描述所有能在电脑上使用USB的设备的安装
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    //测试类
    public class USBdemo {
    public static void main(String[] args) {
    PCfailityUsb usb = new PCfailityUsb();
    usb.USBsetting(new PhoneUSB());
    System.out.println("------------------------");
    try {
    Thread.sleep(5000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    usb.USBsetting(new PrintUSB());
    }
    }

    //定义一个USB规范
    interface USB{
    void setUp(); //安装驱动
    void work(); //驱动进行工作
    }

    //定义一个电脑类
    class PCfailityUsb {
    public void USBsetting(USB usb){
    usb.setUp();
    usb.work();
    }
    }

    //实现一个USB的子类
    class PhoneUSB implements USB{

    @Override
    public void setUp() {
    System.out.println("手机USB安装...");
    }

    @Override
    public void work() {
    System.out.println("手机USB工作");
    }
    }

    //实现一个USB的子类
    class PrintUSB implements USB{

    @Override
    public void setUp() {
    System.out.println("打印机USB安装...");
    }

    @Override
    public void work() {
    System.out.println("打印机USB工作");
    }
    }

如上代码便展示了接口的一般用处: 定义了一套规范,每一个接口实现类都需要遵循这套规范.实现类可以作为形参传给一个方法,此时实现类覆写了超类中的方法(即接口中的方法,已经具有了自己的多态性),再次调用实现类自己的方法便可以看到不同的覆写结果,这也是多态性的体现
通过以上代码我们发现:接口和对象多态性的概念结合之后,对于参数的统一更加明确了。而且可以发现接口是在
类之上的设计抽象。

四.工厂设计模式

场景: 小明去商场想要购买一个电脑,此时他看上了两款电脑,一款是Mca,一款是Lenovo. 由于没有现货,他需要选定一款电脑 然后厂家根据他的需要为他生产其中一款他想要购买的电脑.现在假设厂家是客户端client,不使用工厂模式来简单模拟实现一下此过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class FactoryDesign {
public static void main(String[] args) {
Client client = new Client();
client.buyComputer(new Mac());
client.buyComputer(new Lenovov());
}
}


interface Computer{
void printComputer();
}

class Client{
public void buyComputer(Computer pc){
pc.printComputer();
}
}

class Mac implements Computer{

@Override
public void printComputer() {
System.out.println("this is Mac");
}
}

class Lenovov implements Computer{

@Override
public void printComputer() {
System.out.println("this is Lenovo");
}
}

这时候问题就来了,如果小明又想要一个Alien笔记本呢? 我们就不得不返回客户端去修改代
码,让客户端支持Alienware笔记本。那么,如何将实例化具体类的代码从客户端中抽离,或者封装起来,使它们
不会干扰应用的其他部分呢?这就要用到工厂设计模式!!!!

1.简单工厂模式

简单工厂模式: 专门定义一个类用来创建其他类的实例,被创建的实例通常都具有相同的父类
例如在本例中: 这里我们相当于是创建生产电脑的工厂,客户需要购买什么样的电脑,只要输入类型编号就可以获取该电脑。将类的实例化交给工厂易于解耦。
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class FactoryDesign {

public static void main(String[] args) {
Clienter clienter = new Clienter();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入想要购买的电脑名称");
String wantPC = scanner.nextLine();
ComputerAll Pcwant = Factory.creatPc(wantPC);
clienter.buyPC(Pcwant);
}
}

class Clienter{
public void buyPC(ComputerAll pc){
pc.printPc();
}
}

interface ComputerAll{
void printPc();
}

class Factory{
public static ComputerAll creatPc(String pcName){
ComputerAll computer = null;
if(pcName.equals("Macpro")){
computer = new Macpro();
}else if(pcName.equals("lenovo")){
computer = new Lenovo();
}
return computer;
}
}

class Lenovo implements ComputerAll{
@Override
public void printPc() {
System.out.println("this is lenovo pc");
}
}

class Macpro implements ComputerAll{

@Override
public void printPc() {
System.out.println("this is Macpro");
}
}

以上就是一个简单工厂的代码示例. 我们将一个类专门用来进行不同类型电脑的生产,这个类被称为我们的工厂,他会根据要求产生出来我们想要的类的实例化对象.创建好实例化对象之后我们将实例化对象交给客户端,由客户端去寻找对应的实现方法.这个就是一个简单的工厂模式!
工厂模式概要

  • 一个抽象产品类(接口定义的产品规范)
  • 一个工厂(用来生产不同的产品类对象)
  • 具体产品类(继承一个接口的不同产品类)

优点

  • 把类的实例化工作交给工厂,易于解耦
  • 简单易于实现

缺点

  • 新增产品的话需要修改工厂,违反了OCP开闭原则

2.工厂方法模式

简单工厂模式是所有类型的对象实例化都在一个工厂内进行,工厂方法模式是每种类型的产品都有自己的对象工厂
(bit描述)工厂方法模式:定义一个用来创建对象的接口,让子类决定实例化哪一个类,让子类决定实例化延迟到子类。
工厂方法模式是针对每个产品提供一个工厂类,在客户端中判断使用哪个工厂类去创建对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class FactoryDesign2 {
public static void main(String[] args) {
Client2 client2 = new Client2(); //客户端

FactoryPC macPC = new MacFactory(); //想要哪一种类型的电脑就去哪个类型的工厂中去建造
PC finallyPC = macPC.creatPC();

client2.buyComputer(finallyPC);
}
}

class Client2{
public void buyComputer(PC pc){
pc.printPC();
}
}

interface PC{
void printPC();
}

class MacPC implements PC{
@Override
public void printPC() {
System.out.println("this is MacPC");
}
}

class LenovoPC implements PC{
@Override
public void printPC() {
System.out.println("this is LenovoPC");
}
}

interface FactoryPC{
public PC creatPC();
}

class MacFactory implements FactoryPC{
@Override
public PC creatPC() {
return new MacPC();
}
}

class LenovoFactory implements FactoryPC{

@Override
public PC creatPC() {
return new LenovoPC();
}
}

工厂方法模式是针对每一个产品都创建一个工厂,在客户端中去判断使用哪一个工厂类去创建对象
简单工厂模式和工厂方法模式的区别

  • 对于简单工厂模式而言,创建对象的逻辑判断放在了工厂类中,客户不感知具体的类,但是其违背了开闭原
    则,如果要增加新的具体类,就必须修改工厂类。
  • 对于工厂方法模式而言,是通过扩展来新增具体类的,符合开闭原则,但是在客户端就必须要感知到具体的
    工厂类,也就是将判断逻辑由简单工厂的工厂类挪到客户端。
  • 工厂方法横向扩展很方便,假如该工厂又有新的产品 Macbook Air 要生产,那么只需要创建相应的工厂类和
    产品类去实现抽象工厂接口和抽象产品接口即可,而不用去修改原有已经存在的代码。

工厂方法模式概括

  • 一个抽象产品类
  • 多个具体产品类
  • 一个抽象工厂类
  • 多个具体工厂类,每一个工厂类对应一个具体的产品

优点

  • 降低了代码耦合度,对象的生成交给子类去完成
  • 实现类OCP原则,每次新增产品的时候不需要去修改原有的代码

缺点

  • 代码量增加,每一个类型的产品都需要有一个自己对应的工厂
  • 当增加抽象产品 也就是添加一个其他产品族 需要修改工厂 违背OCP

3.抽象工厂模式

上面所说的简单工厂模式和工厂方法模式不论他们的工厂再生产产品的时候有什么区别,但都只是生产一种类型的产品(例如都是只生产某种电脑).现在有需求,工厂不但要生产电脑还需要生产操作系统,那么这时候就要用到抽象工厂模式
我们在抽象工厂接口中新增创建系统的方法,并由实例工厂类去实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
public class FactoryAbstractTest {
public static void main(String[] args) {
NewClient newClient = new NewClient();
AppleFactory appleFactory= new AppleFactory();
newClient.buyPC(appleFactory.getPc());
newClient.buyOS(appleFactory.getOs());

}
}

class NewClient{
public void buyPC(IComputer pc){
pc.printComputer();
}

public void buyOS(SystemOS os){
os.printOS();
}
}



interface IComputer{
void printComputer();
}

class Apple implements IComputer{

@Override
public void printComputer() {
System.out.println("this is ApplePC");
}
}

class LenovoII implements IComputer{

@Override
public void printComputer() {
System.out.println("this is LenovoII");
}
}

interface SystemOS{
void printOS();
}

class AppleOS implements SystemOS{

@Override
public void printOS() {
System.out.println("this is appleOS");
}
}

class LenovoOS implements SystemOS{

@Override
public void printOS() {
System.out.println("this is LenovoOS");
}
}

interface CommenFactory{
IComputer getPc();
SystemOS getOs();
}

class AppleFactory implements CommenFactory{

@Override
public IComputer getPc() {
return new Apple();
}

@Override
public SystemOS getOs() {
return new AppleOS();
}
}

class LenovoFactory1 implements CommenFactory{

@Override
public IComputer getPc() {
return new LenovoII();
}

@Override
public SystemOS getOs() {
return new LenovoOS();
}
}

总结:抽象工厂模式其实和工厂方法模式基本一样,不同点就在于工厂方法模式中一个工厂只加工一种产品(例如只加工一个电脑,不会生产操作系统),而抽象工厂模式他可以生产一个品牌的多种商品.由此,我们便需要给所有工厂定义一个规范,即所有工厂的超类,一个定义规范的接口.
概要总结:
1.多个抽象产品类
2.具体产品类
3.抽象工厂类 –> 声明一组返回抽象产品的方法
4.具体工厂类 –> 返回一组具体的抽象产品

优点:

  • 代码解耦
  • 实现多个产品族(相关联产品组成的家族),而工厂方法模式的单个产品,可以满足更多的生产需求
  • 很好的满足OCP开放封闭原则
  • 抽象工厂模式中我们可以定义实现不止一个接口,一个工厂也可以生成不止一个产品类对于复杂对象的生产相当灵活易扩展

JDK中工厂模式的使用地方:Iterator(集合类中的迭代器)

五.代理模式

两个子类实现同一个接口,其中一个负责真实业务的实现,另外一个完成辅助真实业务的辅助操作的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class AgencyDesignTest {
public static void main(String[] args) {
BuyPc agency = Fact.getInstance(); //代理类对象实例化
agency.buyPC(); //调用代理类的方法,隐藏了真实业务的实现类
}
}

interface BuyPc{
void buyPC(); //真实业务: 购买一个电脑
}

//真实业务的实现类
class Real implements BuyPc{

@Override
public void buyPC() {
System.out.println("购买一台电脑");
}
}

//代理类
class Agency implements BuyPc{

//代理类中传入真实业务对象以便后面来调用真实业务
private BuyPc buyPc;

public Agency(BuyPc buyPc){
this.buyPc = buyPc;
}

//辅助工作
public void CreatPc(){
System.out.println("制造电脑");
}

//辅助工作
public void Pay(){
System.out.println("付款");
}


@Override
public void buyPC() {
this.CreatPc();
this.buyPc.buyPC(); //调用真实业务
this.Pay();
}
}


class Fact{
public static Agency getInstance(){
return new Agency(new Real()); //创造代理类对象的工厂
}
}

代理模式: 通过一个代理对象来完成一件事情并隐藏真实对象的行动(及真实业务对象不需要自己去完成真是业务,而是由一个代理的对象来帮助他完成所有的事情->包括辅助业务和真实业务)
代理模式的本质:所有的真实业务操作都会有一个与之辅助的工具类(功能类)共同完成。

六.抽象类和接口的区别

  • 抽象类中可以有普通方法也可以有抽象方法,同样的,也可以没有抽象方法,但他仍然是抽象类;而接口中只能是抽象方法和全局常量
  • 抽象类可以有各种权限,接口的权限只有public
  • 子类继承抽象类使用关键字extends,实现接口使用implements,并且抽象类也只能继承一个类,而一个类可以实现多个接口,并且一个接口可以使用extends关键字来继承多个接口