设计模式

lol

一、简单工厂模式

1.解释

简单工厂模式(Simple Factory Pattern)属于类的创新型模式,又叫静态工厂方法模式(Static FactoryMethod Pattern),是通过专门定义一个类来负责创建其他类的实例,被创建的实例通常具有共同的父类。

2.包含的角色

(1)工厂角色(creator):简单工厂模式的核心,由它负责创建所有类的内部逻辑。而且工厂必须能够被外界调用,创建所需要的产品对象。

(2)抽象产品角色(product):简单工厂模式所创建的所有对象的父类,注意,这里的父类可以是接口类也可以是抽象类,它负责描述所有实例所共有的公共接口。

(3)具体产品角色(concrete product):简单工厂所创建的具体实例对象,这些具体的产品往往拥有共同的父类。

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
//抽象产品角色
public interface Food{
//获得相应的食物
public void get();
}
//具体产品角色
//麦香鸡
public class McChicken implements Food{
//获得一份麦香鸡
@Override
public void get(){
System.out.println("一份麦香鸡")
}
}
//薯条
public class Chips implements Food{
//获得一份薯条
@Override
public void get(){
System.out.println("一份薯条")
}
}
//工厂角色
public class FoodFactory{
public static Food getFood(String type) throws InstantiationException,IllegalAccessException,ClassNotFoundException{
if(type.equalsIgnoreCase('mcchicken')){
return McChicken.class.newInstance();
}else if(type.equalsIgnoreCase("chips")){
return Chips.class.newInstance();
}else{
System.out.println("找不到相应的实例化类了!");
return null;
}
}
}
//测试客户端
public class SimpleFactoryTest{
public static void main(String[] args)throws InstantiationException,IllegalAccessException,ClassNotFoundException{
//实例化各种食物
Food mcChicken = FoodFactory.getFood("McChicken");
Food chips = FoodFactory.getFood("Chips");
Food eggs = FoodFactory.getFood("Eggs");
//获取食物
if(mcChicken != null){
mcChicken.get();
}
if(chips != null){
chips.get();
}
if(eggs != null){
eggs.get();
}
}
}
/*结果
找不到相应的实例化类了
一份麦香鸡
一份薯条*/

4.优缺点分析

优点:用户可以直接根据工厂类去创建所需的实例。

缺点:违背了单一原则、开放封闭原则。工厂类集中了所有实例的创建逻辑,如果一旦这个工厂出了问题,所有的客户端都会受到牵连;简单工厂模式的产品都基于一个共同的抽象类或者接口,即有不同的产品接口或者抽象类的时候,工厂类就需要判断何时创建何种产品,这就和创建何种种类产品的产品相互混淆;开放封闭原则中,每当我新增加一个产品的时候必须修改工厂类,相应的工厂类就需要重新编译一遍。

二、工厂方法模式

1.解释

工厂方法模式属于类的创建型模式,又称为多态工厂模式。工厂方法模式的意义在于定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,仅负责声明具体工厂类必须实现的接口。

2.包含的角色

(1)抽象产品角色:具体产品的父类。

(2)具体产品角色:抽象产品的子类。

(3)抽象工厂角色:具体工厂的父类。

(4)具体工厂角色:抽象工厂的子类。

工厂和产品之间的关系是具体工厂中调用具体产品类,生成具体的产品。

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
//抽象工厂角色
public interface Factory{
//生产产品
public Product manufacture();
}
//抽象产品角色
public interface Product{
//产品的属性
public void show();
}
//具体产品角色
public class ProductA extends Product{
@Override
public void show(){
System.out.println("生产出了产品A");
}
}
public class ProductB extends Product{
@Override
public void show(){
System.out.println("生产出了产品B");
}
}
//具体工厂角色
public class FactoryA extends Factory{
@Override
public Product manufacture(){
return new ProductA();
}
}
public class FactoryB extends Factory{
@Override
public Product manufacture(){
return new ProductB();
}
}
//测试
public class Client{
public static void main(String[] args){
//生产产品A
FactoryA mFactoryA = new FactoryA();
mFactoryA.manufacture().show();
//生产产品B
FactoryB mFactoryB = new FactoryB();
mFactoryB.manufacture().show();
}
}
/*结果
生产出了产品A
生产出了产品B*/

4.优缺点分析

优点:

(1)符合开闭原则,新增一种产品的时候,只需要增加相应的具体产品类和相应的工厂子类即可。

(2)符合单一职责原则,每个具体工厂类只负责创建对应的产品。

(3)不使用静态工厂方法,可以形成基于继承的等级结构。

缺点:

(1)添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加。

(2)虽然保证了工厂方法内对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类。

(3)一个具体工厂只能创建一种具体产品。

三、抽象工厂模式

1.解释

提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式,是工厂方法模式的升级版,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

2.包含的角色

(1)抽象工厂:声明生成抽象产品的工厂。

(2)具体工厂:实现了抽象工厂声明的生成抽象产品的方法,生成一组具体的产品。

(3)抽象产品:在抽象产品中定义了产品的抽象业务方法。

(4)具体产品:定义具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。

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
93
94
//抽象工厂
//游戏界面
public interface GameInterface{
public void interface();
}
//游戏操作
public interface GameOperation{
public void operation();
}
//具体工厂
//游戏界面具体工厂
public class AndroidInterface implement GameInterface{
@Override
public void interface(){
System.out.println("Android游戏界面控制");
}
}
public class IOSInterface implement GameInterface{
@Override
public void interface(){
System.out.println("IOS游戏界面控制");
}
}
public class WindowInterface implement GameInterface{
@Override
public void interface(){
System.out.println("Window游戏界面控制");
}
}
//游戏操作具体工厂
public class AndroidOperation implement GameOperation{
@Override
public void operation(){
System.out.println("Andriod游戏控制");
}
}
public class IOSOperation implement GameOperation{
@Override
public void operation(){
System.out.println("IOS游戏控制");
}
}
public class WindowOperation implement GameOperation{
@Override
public void operation(){
System.out.println("Window游戏控制");
}
}
//抽象工厂-游戏工厂类
public interface GameFactory{
public GameInterface interfaceController();
public GameOperation operationController();
}
//具体工厂
public class AndroidController implement GameFactory(){
@Override
public GameInterface interfaceController(){
return new AndroidInterface();
}
@Override
public GameOperation operationController(){
return new AndroidOperation();
}
}
public class IOSController implement GameFactory(){
@Override
public GameInterface interfaceController(){
return new IOSInterface();
}
@Override
public GameOperation operationController(){
return new IOSOperation();
}
}
public class WindowController implement GameFactory(){
@Override
public GameInterface interfaceController(){
return new WindowInterface();
}
@Override
public GameOperation operationController(){
return new WindowOperation();
}
}
//测试
public class Client{
public static void main(String[] args){
GameFactory andriodGame = new AndriodController();
GameInterface andriodInterface = andriodGame.interfaceController();
GameOperation andriodOperation = andriodGame.operationController();
andriodInterface.interface();
andriodOperation.operation();
}
}

4.优缺点分析

优点:

(1)隔离了具体类的生成,使得客户并不需要知道什么被创建,具有良好的封装性。

(2)横向扩展简单。同个产品族如果需要增加多个产品,只需要增加新的工厂类和产品类即可。

缺点:

纵向扩展困难,如果增加了新的产品族,抽象工厂类也要添加创建该产品族的对应方法,这样一来所有的具体工厂类都要做修改了,严重违背了开闭原则。

四、建造者模式

1.解释

建造者模式是指 指挥者 指挥 建造者 建造产品。不同的具体建造者定义了不同的创建过程,且具体的建造者相互独立,增加新的建造者非常方便,无须修改已有的代码,系统具有较好的扩展性。

2.包含的角色

(1)抽象建造者(Builder):接口中包含一个要建造的产品,建造这个产品各个部分的方法。

(2)具体建造者(Concrete Builder):它实现了抽象建造者接口,实现各个部件的具体构造和装备方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。

(3)产品角色(Product):它是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。

(4)指挥者(Director):它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制)。

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
//产品-小汽车
class Product{
private String name;
private String type;
public void showProduct(){
System.out.println("名称:"+name);
System.out.println("型号:"+type);
}
public void setName(String name){
this.name = name;
}
public void setType(String type){
this.type = type;
}
}
//抽象建造者
public interface Builder{
public void setPart(String arg1,String arg2);
public Product getProduct();
}
//具体建造者
public class ConcreteBuilder extends Builder{
private Product product = new Product();

public Product getProduct(){
return product;
}
public void setPart(String arg1,String arg2){
product.setName(arg1);
product.setType(arg2);
}
}
//指挥者
public class Director{
private Builder builder = new ConcreteBuilder();
public Product getAProduct(){
builder.setPart("宝马汽车","X7");
return builder.getProduct();
}
public Product getBProduct(){
builder.setPart("奥迪汽车","Q5");
return builder.getProduct();
}
}
//测试
public class Client{
Director director = new Director();
Product product1 = director.getAProduct();
product1.showProduct();
Product product2 = director.getBProduct();
product2.showProduct();
}

4.优缺点分析

优点:

建造者的封装性很好,使用建造者模式可以有效的封装变化,所以,一般产品类和建造者类是比较稳定的。将主要的业务逻辑封装在指挥者类中对整体而言可以取得比较好的稳定性。

建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码。

由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

缺点:

建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

如果产品的内部变化复杂,可能会导致需要定义很多具体建造类来实现这种变化,导致系统变得很庞大,可维护性变差。

五、原型模式

1.解释

原型模式是一种对象创建型模式,建造者模式是一种对象创建型模式,用原型模式可以用原型实例指定创建原型的种类,它允许通过一个原型对象创建多个同类型的其他对象,而无需知道该对象的创建细节,在java中可以直接使用Object提供的clone方法来实现对象的克隆(浅克隆)。

2.包含的角色

(1)抽象原型类(Prototype):给出所有的具体原型类所需的接口。

(2)具体原型类(Concrete Prototype):被复制的对象,此角色需要实现抽象的原型角色所要求的接口。

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
//浅克隆
public class Brand{
public void install(){
System.out.println("安装汽车品牌");
}
}
public class Car implements Cloneable{
private Brand brand;
public Car(){
this.brand = new Brand();
}
@Override
protected Object clone() throws CloneNotSupportedException{
return (Car)super.clone();
}
public Brand getBrand(){
return this.brand;
}
public void run(){
System.out.println("小汽车能跑了");
}
}
//深克隆
public class Brand implement{
public void install(){
System.out.println("安装汽车品牌");
}
@Override
protected Object clone() throws CloneNotSupportedException{
super.clone();
}
}
public class Car implements Cloneable{
private Brand brand;
public Car(){
this.brand = new Brand();
}
@Override
protected Object clone() throws CloneNotSupportedException{
Car car = null;
try{
car = (Car)super.clone();
car.brand = (Brand)brand.clone();
}catch(Exception e){
System.out.println(e.getMessage());
}
return car;
}
public Brand getBrand(){
return this.brand;
}
public void run(){
System.out.println("小汽车能跑了");
}
}

4.优缺点分析

优点:原型模式简化了创建对象的过程,通过一个已有的实例进行复制提高了创建实例的效率,具有较好的可扩展性。

缺点:部分时候实现克隆可能较为麻烦。

六、单例模式

1.解释

单例模式是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。

该模式有三个基本要点:一是这个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。

2.分类及基本代码展示分析

1.饿汉式单例模式

1
2
3
4
5
6
7
8
9
public class HungrySingleton{
private final static HungrySingleton HUNGRY_SINGLETON = new HungrySingleton();
private HungrySingleton(){

}
public static HungrySingleton getInstance(){
return HUNGRY_SINGLETON;
}
}

饿汉式在类创建的同时就已经创建好了一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

饿汉式模式适用于单例模式较少的场景;如果我们在程序启动后,一定会加载到类,那么用饿汉模式实现的单例模式简单又实用;如果我们是写一些工具类,则优先考虑使用懒汉模式,可以避免提前被加载到内存中,占用系统资源。

2.懒汉式单例模式(双重检查锁)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LazyDoubleCheckSingleton{
private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton =null;
//volatile关键字可以保证线程间变量的可见性,还有一个作用就是阻止局部重排序的发生。
private LazyDoubleCheckSingleton(){
}
public static LazyDoubleCheckSingleton getInstance(){
if(null==lazyDoubleCheckSingleton){
synchronized(LazyDoubleCheckSingleton.class){
if(null==lazyDoubleCheckSingleton){
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
}
}
}
return lazyDoubleCheckSingleton;
}
}

相比于单锁而言,双重检查性能上虽然有提升,但是依旧用到了synchronized关键字总归要上锁,对程序性能还是存在一定的性能影响。注意里面volatile的使用。

3.区别

(1)饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

(2)懒汉式本身是非线程安全的,但是上述写法实现了线程安全;饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题。

(3)饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

七、适配器模式

1.解释

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

2.包含的角色

(1)目标接口(Target):当前系统业务所期待的接口,他可以是抽象类或接口。

(2)适配者类(Adaptee):它是被访问和适配的现存组件库中的组件接口。

(3)适配器类(Adapter):它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

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
//目标接口
interface Target{
public void request();
}
//适配者类
class Adaptee{
public void specificRequest(){
System.out.println("适配者中的业务代码被调用!")
}
}
//类适配器类
class ClassAdapter extends Adaptee implements Target{
public void request(){
specificRequest();
}
}
//测试
public class ClassAdapterTest{
public static void main(String[] args){
System.out.println("类适配器模式测试:");
Target target = new ClassAdapter();
target.request();
}
}
/*结果
类适配器模式测试:
适配器中的业务代码被调用!*/

4.优缺点分析

优点:

(1)客户端通过适配器可以透明的调用目标接口。

(2)复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。

(3)将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。

(4)在很多业务场景中符合开闭原则。

缺点:

(1)适配器编写过程中需要结合业务场景全面考虑,可能会增加系统的复杂性。

(2)增加代码阅读难度,降低代码可读性,过多的使用适配器会使系统代码变得凌乱。

八、桥接模式

1.解释

桥接模式(Bridge Pattern):将抽象部分和他的实现部分分离,使它们都可以独立的变化。它是一种对象结构型模式,又称为柄体(Handle and Boy)模式或者接口(Interface)模式。如果某个类存在两个独立变化的维度,可以运用桥接模式将这两个维度分离出来,让系统更加符合”单一职责原则”。桥接模式中是在抽象类里面建立一个抽象关联,该关联关系就像是一条桥一样,将两个独立继承结构的类联结起来。

2.包含的角色

(1)抽象类(Abstraction):用于定义抽象类的接口,它一般是抽象类而不是接口。它既可以包含抽象业务方法,也可以包含具体业务方法。

(2)扩充抽象类(Refined Abstraction):扩充由Abstraction定义的接口,通常情况下它不再是抽象类而是具体类,它实现了在Abstraction中声明的抽象业务方法。

(3)实现类接口(Implementor):Implementor接口仅提供基本操作,而Abstraction定义的接口可能会做更多更复杂的操作。

(4)具体实现类(Concrete Implementor):具体实现Implementor接口,在不同的Concrete Implementor中提供基本操作的不同实现。

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
//交通工具(Implementor)
public interface Vehicle{
public void drive();
}
//具体的交通工具(ConcreteImplementor)
public class Car implements Vehicle
{
@Override
public void drive()
{
System.out.print("小轿车");
}
}
public class Bus implements Vehicle
{
@Override
public void drive()
{
System.out.print("大巴");
}
}
//抽象的路(Abstraction)
public abstract class Road
{
protected Vehicle vehicle;

public Road(Vehicle vehicle)
{
this.vehicle = vehicle;
}

public abstract void driveOnRoad();
}
//具体的路(Concrete Implementor)
public class UnpavedRoad extends Road
{
public UnpavedRoad(Vehicle vehicle)
{
super(vehicle);
}

@Override
public void driveOnRoad()
{
super.vehicle.drive();
System.out.println("行驶在石子路");
}
}
public class CementRoad extends Road
{
public CementRoad(Vehicle vehicle)
{
super(vehicle);
}

@Override
public void driveOnRoad()
{
super.vehicle.drive();
System.out.println("行驶在水泥路");
}
}
//测试
Road road = new CementRoad(new Car());
road.driveOnRoad();
/*结果
小轿车行驶在水泥路*/

4.优缺点分析

优点:

(1)分离抽象接口及其实现部分。桥接模式使用”对象间的关联关系”解耦了抽象和实现类之间固有的绑定关系,使得抽象和实现不再同一个继承层次结构中,使他们都具有自己的子类。

(2)在很多情况下,桥接模式可以取代多层继承方案,它极大减少了子类的个数。

(3)桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合”开闭原则”。

缺点:

(1)桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要去开发者一开始就针对抽象层进行设计与编程。

(2)桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累。

九、组合模式

1.解释

组合模式对单个对象(叶子对象)和组合对象(组合对象)具有一致性,它将对象组织到树结构中,可以用来描述整体与部分的关系。组合对象最关键的一点就是叶子对象和组合对象实现相同的接口。

2.包含的角色

(1)对象声明接口(Component):声明一个接口用于访问和管理Component子部件。

(2)叶子对象(Leaf):叶子对象,叶子结点没有子结点。

(3)容器对象(Composite):容器对象,定义有枝节点行为,用来存储子部件。

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//对象声明接口
public abstract class File {
String name;

public File(String name){
this.name = name;
}

public String getName() {
return name;
}

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

public abstract void display();
}
//容器对象
public class Folder extends File{
private List<File> files;

public Folder(String name){
super(name);
files = new ArrayList<File>();
}

/**
* 浏览文件夹中的文件
*/
public void display() {
for(File file : files){
file.display();
}
}

/**
* @desc 向文件夹中添加文件
* @param file
* @return void
*/
public void add(File file){
files.add(file);
}

/**
* @desc 从文件夹中删除文件
* @param file
* @return void
*/
public void remove(File file){
files.remove(file);
}
}
//叶子对象
public class TextFile extends File{

public TextFile(String name) {
super(name);
}

public void display() {
System.out.println("这是文本文件,文件名:" + super.getName());
}

}
public class ImagerFile extends File{

public ImagerFile(String name) {
super(name);
}

public void display() {
System.out.println("这是图像文件,文件名:" + super.getName());
}

}
public class VideoFile extends File{

public VideoFile(String name) {
super(name);
}

public void display() {
System.out.println("这是影像文件,文件名:" + super.getName());
}

}
//客户端测试
public class Client {
public static void main(String[] args) {
/**
* 我们先建立一个这样的文件系统
* 总文件
*
* a.txt b.jpg c文件夹
* c_1.text c_1.rmvb c_1.jpg
*
*/
//总文件夹
Folder zwjj = new Folder("总文件夹");
//向总文件夹中放入三个文件:1.txt、2.jpg、1文件夹
TextFile aText= new TextFile("a.txt");
ImagerFile bImager = new ImagerFile("b.jpg");
Folder cFolder = new Folder("C文件夹");

zwjj.add(aText);
zwjj.add(bImager);
zwjj.add(cFolder);

//向C文件夹中添加文件:c_1.txt、c_1.rmvb、c_1.jpg
TextFile cText = new TextFile("c_1.txt");
ImagerFile cImage = new ImagerFile("c_1.jpg");
VideoFile cVideo = new VideoFile("c_1.rmvb");

cFolder.add(cText);
cFolder.add(cImage);
cFolder.add(cVideo);

//遍历C文件夹
cFolder.display();
//将c_1.txt删除
cFolder.remove(cText);
System.out.println("-----------------------");
cFolder.display();
}
}

4.优缺点分析

优点:

(1)可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易。

(2)客户端调用简单,客户端可以一致的使用组合结构或其中单个对象。

(3)定义了包含叶子对象和容器对象的类层次结构,叶子对象不断递归下去,可以形成复杂的树形结构。

(4)更容易在组合体中加入对象构件,客户端不必因为加入了新的对象构件而改变原有的代码。

缺点:

(1)使设计变的更抽象,对象的业务规则如果很复杂,则实现组合模式具有很大的挑战性,而且不是所有办法都与叶子对象子类都有关联。

十、装饰者模式

1.解释

装饰者模式又名包装(Wrapper)模式。装饰者以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

大多数的装饰者模式实际上是半透明的装饰模式,这样的装饰模式也称作半装饰、半适配器模式。

2.包含的角色

(1)抽象构件角色(Component):给出一个抽象接口,以规范准备接受附加责任的对象。

(2)具体构件角色(ConcreteComponent):定义一个将要接收附加责任的类。

(3)装饰角色(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

(4)具体装饰角色(ConcreteDecorator):负责给构件对象”贴上”附加的责任。

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
//抽象界面构件类:抽象构件类,为了突出与模式相关的核心代码,对原有控件代码进行了大量的简化

abstract class Component

{

public abstract void display();

}



//窗体类:具体构件类

class Window extends Component

{

public void display()

{

System.out.println("显示窗体!");

}

}



//文本框类:具体构件类

class TextBox extends Component

{

public void display()

{

System.out.println("显示文本框!");

}

}



//列表框类:具体构件类

class ListBox extends Component

{

public void display()

{

System.out.println("显示列表框!");

}

}



//构件装饰类:抽象装饰类

class ComponentDecorator extends Component

{

private Component component; //维持对抽象构件类型对象的引用



public ComponentDecorator(Component component) //注入抽象构件类型的对象

{

this.component = component;

}



public void display()

{

component.display();

}

}



//滚动条装饰类:具体装饰类

class ScrollBarDecorator extends ComponentDecorator

{

public ScrollBarDecorator(Component component)

{

super(component);

}



public void display()

{

this.setScrollBar();

super.display();

}



public void setScrollBar()

{

System.out.println("为构件增加滚动条!");

}

}



//黑色边框装饰类:具体装饰类

class BlackBorderDecorator extends ComponentDecorator

{

public BlackBorderDecorator(Component component)

{

super(component);

}



public void display()

{

this.setBlackBorder();

super.display();

}



public void setBlackBorder()

{

System.out.println("为构件增加黑色边框!");

}

}
class Client

{

public static void main(String args[])

{

Component component,componentSB; //使用抽象构件定义

component = new Window(); //定义具体构件

componentSB = new ScrollBarDecorator(component); //定义装饰后的构件

componentSB.display();

}

}

4.优缺点分析

优点:

(1)装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除 掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。

(2)通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
缺点:

使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

十一、外观模式

1.解释

外观模式(Facade Pattern)也称为过程模式,是结构性模式。外观模式为子系统的一组接口提供了一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式可以理解为 为转换一群接口,客户只要调用这一个接口而不用调用多个接口才能达到目的,也不需要关心这个子系统的内部细节。就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用。

2.包含的角色

Facade外观类:提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给相应的子系统对象。

System子系统:处理Facade对象指派的任务,是功能的实际提供者。

Client客户端:外观接口调用测试者。

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//各子系统
public class Player { //播放器
private static Player instance = new Player();//使用单例模式(饿汉式)
public static Player getInstanc() {
return instance;
}
public void on() {
System.out.println(" 播放器打开了 ");
}
public void off() {
System.out.println(" 播放器关闭了 ");
}
public void play() {
System.out.println(" 播放器播放中 ");
}
public void pause() {
System.out.println(" 播放暂停 ");
}
}

public class Screen { //屏幕
private static Screen instance = new Screen();
public static Screen getInstance() {
return instance;
}
public void up() {
System.out.println(" 屏幕上升 ");
}
public void down() {
System.out.println(" 屏幕下降 ");
}
}

public class Stereo { //音响
private static Stereo instance = new Stereo();
public static Stereo getInstance() {
return instance;
}
public void on() {
System.out.println(" 音响打开了 ");
}
public void off() {
System.out.println(" 音响关闭了 ");
}
public void setVolume() {
System.out.println(" 音响音量调节 ");
}
}

public class Projector { //投影仪
private static Projector instance = new Projector();
public static Projector getInstance() {
return instance;
}
public void on() {
System.out.println(" 投影仪打开了 ");
}
public void off() {
System.out.println(" 投影仪关闭了 ");
}
public void focus() {
System.out.println(" 投影仪聚焦 ");
}
public void zoom() {
System.out.println(" 投影仪放大 ");
}
}
//外观类
public class HomeTheaterFacade { //外观类
//定义各个子系统对象
private Player player;
private Screen screen;
private Stereo stereo;
private Projector projector;

//构造器
public HomeTheaterFacade() {
this.player = Player.getInstance();
this.screen = Screen.getInstance();
this.stereo = Stereo.getInstance();
this.projector = Projector.getInstance();

}

//一致界面
public void ready() {
System.out.println("===ready===");
screen.down();
player.on();
stereo.on();
stereo.setVolume();
projector.on();
projector.zoom();
projector.focus();
}
public void play() {
System.out.println("===play===");
player.play();
}
public void pause() {
System.out.println("===pause===");
player.pause();
}
public void end() {
System.out.println("===end===");
screen.up();
projector.off();
stereo.off();
player.off();
}
}
//客户端
public class Client {
public static void main(String[] args) {
HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
homeTheaterFacade.ready();
homeTheaterFacade.play();
homeTheaterFacade.pause();
homeTheaterFacade.end();
}
}

4.优缺点分析

外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展。

外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性。

当系统需要进行分层设计的时候,可以考虑外观模式帮助我们更好的划分访问的层次。

不需要过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要让系统有层次,利于维护为目的。

在维护一个遗留的大型系统的时候,可能这个系统已经变的非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性。

优点:

1)松耦合,用户和子系统解耦,屏蔽子系统;可以提高子系统的独立性。

2)使用简单,简化了用户与子系统的依赖关系,用户只与门面对接,有统一的入口;不需要知道所有子系统及内部构造。

缺点:

1)不规范的编程方式,没有面向抽象编程,而是通过增加中介层,转换服务提供方的服务接口。

最核心的目的:简化子系统,简化客户使用,屏蔽多个子系统。

十二、轻量级模式

1.解释

轻量级模式又叫做享元模式,是对象池的一种标签。类似线程池,线程池可以避免不停地创建和销毁对象,消耗性能。享元模式可以减少对象的数量,其宗旨是共享细粒度对象,将多个对同一对象的访问集中起来,属于结构型设计模式。

2.包含的角色

抽象享元角色(IFlyweight):享元对象抽象基类或接口,同时定义出对象的外部状态和内部状态的接口实现。

具体享元模式(ConcreteFlyWeight):实现了抽象角色定义的业务。该角色的内部状态处理应该与环境无关,不会出现一个操作改变内部状态,同时修改了外部状态的情况。

享元工厂(FlyweightFactory):负责管理享元对象池和创建享元对象。

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
/**
* 抽象享元角色
*/
public interface IFlyweight {

void operation(String extrinsicState);

}
/**
* 具体享元角色
*/
public class ConcreteFlyweight implements IFlyweight {

private String intrinsicState;

public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}

@Override
public void operation(String extrinsicState) {
System.out.println("object address: " + System.identityHashCode(this));
System.out.println("extrinsicState: " + extrinsicState);
System.out.println("intrinsicState: " + intrinsicState);
}
}
/**
* 享元工厂
*/
public class FlyweightFactory {

public static Map<String, IFlyweight> pool = new ConcurrentHashMap<>();

public static IFlyweight getFlyweight(String intrinsicState) {
if (!pool.containsKey(intrinsicState)) {
IFlyweight flyweight = new ConcreteFlyweight(intrinsicState);
pool.put(intrinsicState, flyweight);
}
return pool.get(intrinsicState);
}
}
public class Client {

public static void main(String[] args) {
IFlyweight flyweight1 = FlyweightFactory.getFlyweight("aa");
IFlyweight flyweight2 = FlyweightFactory.getFlyweight("aa");
IFlyweight flyweight3 = FlyweightFactory.getFlyweight("cc");
flyweight1.operation("wwwww");
flyweight2.operation("ttttt");
flyweight3.operation("wwwww");
}
}

4.优缺点分析

优点:减少对象的创建,降低内存中对象的数量,降低系统内存,提高系统性能。

缺点:

1)需要关注内、外部状态,关注线程安全问题(同时访问相同的缓存)

2)系统程序负责化了

十三、代理模式

1.解释

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

2.包含的角色

1)抽象主题角色(Subject):声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。

2)具体主题对象(RealSubject):也称为委托角色或者被代理角色。定义了代理对象所代表的目标对象。

3)代理主题角色(Proxy):也叫委托类、代理类。代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//抽象主题角色
public interface Subject
{
void operate();
}
//具体主题角色
public class RealSubject implements Subject
{
@Override
public void operate()
{
System.out.println("RealSubject");
}
}
//代理类
public class Proxy implements Subject
{
private Subject subject = null;

@Override
public void operate()
{
if(subject == null)
subject = new RealSubject();
System.out.print("I'm Proxy, I'm invoking...");
this.subject.operate();
}
}
//测试代码
Subject subject = new Proxy();
subject.operate();
//动态代理
public interface Subject
{
String operate1();
String operate2();
String operate3();
String operate4();
String operate5();
}
public class RealSubject1 implements Subject
{
@Override
public String operate1()
{
return "RealSubject-operate1()";
}

@Override
public String operate2()
{
return "RealSubject-operate2()";
}

@Override
public String operate3()
{
return "RealSubject-operate3()";
}

@Override
public String operate4()
{
return "RealSubject-operate4()";
}

@Override
public String operate5()
{
return "RealSubject-operate5()";
}
}

public class RealSubject2 implements Subject
{
@Override
public String operate1()
{
return "RealSubject2-operate1()";
}

@Override
public String operate2()
{
return "RealSubject2-operate2()";
}

@Override
public String operate3()
{
return "RealSubject2-operate3()";
}

@Override
public String operate4()
{
return "RealSubject2-operate4()";
}

@Override
public String operate5()
{
return "RealSubject2-operate5()";
}
}
public class ProxyHandler implements InvocationHandler
{
Object obj = null;

public Object newProxyInstance(Object realObj){
this.obj = realObj;
Class<?> classType = this.obj.getClass();
return Proxy.newProxyInstance(classType.getClassLoader(), classType.getInterfaces(), this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.print("I'm Proxy, I'm invoking...");
Object object = method.invoke(obj, args);
System.out.println(object);
return object;
}
}
Subject object = new RealSubject();
Subject subject = (Subject) new ProxyHandler().newProxyInstance(new RealSubject());
Subject subject2 = (Subject) new ProxyHandler().newProxyInstance(new RealSubject2());
subject.operate2();
subject2.operate4();

4.优缺点分析

优点:

1)代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用

2)代理对象可以扩展目标对象的功能

3)代理模式能将客户端与目标对象进行分离,在一定程度上降低了系统的耦合度。

缺点:
在客户端和目标对象之间增加了一个代理对象,会造成请求处理速度变慢

增加了系统的复杂度

十四、责任链模式(职责链模式)

1.解释

在现实生活中,一个事件需要经过多个处理对象处理是常见的场景,例如,采购审批流程、请假流程等。公司员工请假,可批假的领导有部分负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这无疑增加了难度。

在计算机软硬件中也有相关的例子,如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否能处理该异常;还有Struts2的拦截器、JSP和Servlet的Filter等,所有这些,都可以考虑使用责任链模式来实现。

定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有的请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

2.包含的角色

1.抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。

2.具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。

3.客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解其模式,而不是其具体实现。责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来。

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
//抽象处理者角色
abstract class Handler{
private Handler next;
public void setNext(Handler next){
this.next = next;
}
public Handler getNext(){
return next;
}
//处理请求的方法
public abstract void handleRequest(String request);
}
//具体处理者角色
class ConcreteHandler1 extends Handler{
public void handleRequest(String request){
if(request.equals("one")){
System.out.println("具体处理者1负责处理该请求!");
}else{
if(getNext() != null){
getNext().handleRequest(request);
}else{
System.out.println("没有人处理该请求!")
}
}
}
}
class ConcreteHandler2 extends Handler{
public void handleRequest(String request){
if(request.equals("two")){
System.out.println("具体处理者2负责处理该请求!");
}else{
if(getNext() != null){
getNext().handleRequest(request);
}else{
System.out.println("没有人处理该请求!")
}
}
}
}
//客户端
public class ChainOfResponsibilityPattern{
public static void main(String[] args){
//组装责任链
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
//提交请求
handler1.handleRequest("two");
}
}
//运行结果如下
具体处理者2负责处理该请求

在上面代码中,我们把消息硬编码为String类型,而在真实业务中,消息是具备多样性的,可以是int、String或者自定义类型。因此,在上面代码的基础上,可以对消息类型进行抽象request,增强了消息的兼容性。

应用实例:

假如规定学生请假小于或等于 2 天,班主任可以批准;小于或等于 7 天,系主任可以批准;小于或等于 10 天,院长可以批准;其他情况不予批准。

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
//学生请假条审批
//抽象处理者:领导类
abstract class Leader{
private Leader next;
public void setNext(Leader next){
this.next = next;
}
public Leader getNext(){
return next;
}
//处理请求的方法
public abstract void handleRequest(int LeaveDays);
}
//具体处理者1:班主任类
class ClassAdviser extends Leader{
public void handleRequest(int LeaveDays){
if(LeaveDays <= 2){
System.out.println("班主任批准你请假"+LeaveDays+"天。")
}else{
if(getNext() != null){
getNext().handleRequest(LeaveDays);
}else{
System.out.println("请假天数太多,没有人批准该假条!")
}
}
}
}
//具体处理类2:系主任类
class DepartmentHead extends Leader{
public void handleRequest(int LeaveDays){
if(LeaveDays <= 7){
System.out.println("系主任批准你请假"+LeaveDays+"天。")
}else{
if(getNext()!=null){
getNext().handleRequest(LeaveDays);
}else{
System.out.println("请假天数太多,没有人批准该假条!")
}
}
}
}
//具体处理者类3:院长类
class Bean extends Leader{
public void handleRequest(int LeaveDays){
if(LeaveDays <= 10){
System.out.println("院长批准你请假"+LeaveDays+"天。")
}else{
if(getNext() != null){
getNext().handleRequest(LeaveDays);
}else{
System.out.println("请假天数太多,没有人批准该假条!");
}
}
}
}
//具体处理类4:教务处长类
class DeanOfStudies extends Leader{
public void handleRequest(int LeaveDays){
if(LeaveDays <= 20){
System.out.println("教务处长批注你请假"+LeaveDays+"天。");
}else{
if(getNext() != null){
getNext().handleRequest(LeaveDays);
}else{
System.out.println("请假天数太多,没有人批准该假条!")
}
}
}
}

4.优缺点分析

通常情况下,可以通过数据链表来实现职责链模式的数据结构。

应用场景:

1、多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。

2、可动态指定一组对象处理请求,或添加新的处理者。

3、需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

优点:

1.降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。

2.增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。

3.增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态的改变链内的成员或者调用它们的次序,也可动态的新增或者删除责任。

4.责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的if或者if…else语句。

5.责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点:
1.不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。

2.对于较长的责任链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。

3.职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

十五、命令模式

1.解释

在软件开发系统中,”方法的请求者”和”方法的实现者”之间经常存在紧密的耦合关系,这不利于软件功能的扩展与维护。例如,想对方法进行”撤销、重做、记录”等处理都很不方便,因此,如何将方法的请求者与实现者解耦?

在现实生活中,命令模式的例子也很多。比如看电视时,我们只需要轻轻一按遥控器就能完成频道的切换,这就是命令模式,将换台请求和换台处理完全解耦了。电视机遥控器(命令发送者)通过按钮(具体命令)来遥控电视机(命令接收者)。

定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。

2.包含的角色

1.抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法execute()。

2.具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。

3.实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。

4.调用者/请求者(Invoker)角色:是请求的发送者,他通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

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
//调用者
class Invoker{
private Command command;
public Invoker(Command command){
this.command = command;
}
public void setCommand(Command command){
this.command = command;
}
public void call(){
System.out.println("调用者执行命令command...");
command.execute();
}
}
//抽象命令
interface Command{
public abstract void execute();
}
//具体命令
class ConcreteCommand implement Command{
private Receiver receiver;
ConcreteCommand(){
receiver = new Receiver();
}
public void execute(){
receiver.action();
}
}
//接收者
class Receiver{
public void action(){
System.out.println("接收者的action()方法被调用...")
}
}
//客户端
public class CommandPattern{
public static void main(String[] args){
Command cmd = new ConcreteCommand();
Invoker ir = new Invoker(cmd);
System.out.println("客户访问调用者的call()方法...")
}
}
//结果
客户访问调用者的call()方法...
调用者执行命令command...
接收者的action()方法被调用...

应用实例:

命令模式实现客户去餐馆吃早餐

分析:客户去餐馆可选择的早餐有肠粉、河粉和馄饨等,客户可向服务员选择以上早餐中的若干种,服务员将客户的请求交给相关的厨师去做。这里的点早餐相当于“命令”,服务员相当于“调用者”,厨师相当于“接收者”,所以用命令模式实现比较合适。

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
98
//调用者:服务员
class Waiter{
private Breakfast changFen,hunTun,heFen;
public void setChangFen(Breakfast f){
changFen = f;
}
public void setHunTun(Breakfast f){
hunTun = f;
}
public void setHeFen(Breakfast f){
heFen = f;
}
public void chooseHunTun(){
hunTun.cooking();
}
public void chooseChangFen(){
changFen.cooking();
}
public void chooseHeFen(){
heFen.cooking();
}
}
//抽象命令:早餐
interface Breakfast{
public abstract void cooking();
}
//具体命令:肠粉
class ChangFen implements Breakfast{
private ChangFenChef receiver;
ChangFen(){
receiver = new ChangFenChef();
}
public void cooking(){
receiver.cooking();
}
}
//具体命令:馄饨
class HunTun implements Breakfast{
private HunTunChef receiver;
HunTun(){
receiver = new HunTunChef();
}
public void cooking(){
receiver.cooking();
}
}
//具体命令:河粉
class HeFen implements Breakfast{
private HeFenChef receiver;
HeFen(){
receiver = new HeFenChef();
}
public void cooking(){
receiver.cooking();
}
}
//接收者:肠粉厨师
class ChangFenChef{
ChangFenChef(){
System.out.println("肠粉厨师");
}
public void cooking(){
System.out.println("在做肠粉...");
}
}
//接收者:馄饨厨师
class HunTunChef{
HunTunChef(){
System.out.println("馄饨厨师");
}
public void cooking(){
System.out.println("在做馄饨...");
}
}
//接收者:河粉厨师
class HeFenChef{
HeFenChef(){
System.out.println("河粉厨师");
}
public void cooking(){
System.out.println("在做河粉...");
}
}
//客户端
public class CookingCommand{
public static void main(String[] args){
Breakfast food1 = new ChangFen();
Breakfast food2 = new HunTun();
Breakfast food3 = new HeFen();
Waiter waiter = new Waiter();
waiter.setChangFen(food1);//设置肠粉菜单
waiter.setHunTun(food2);//设置馄饨菜单
waiter.setHeFen(food3);//设置河粉菜单
waiter.chooseChangFen();//选择肠粉
waiter.chooseHunTun();//选择馄饨
waiter.chooseHeFen();//选择河粉
}
}

应用场景:

当系统的某项操作具备命令语义,且命令实现不稳定(变化)时,可以通过命令模式解耦请求与实现。使用抽象命令接口使请求方的代码架构稳定,封装接收方具体命令的实现细节。接收方与抽象命令呈现弱耦合(内部方法无需一致),具备良好的扩展性。

通常适用于以下场景:

1.请求调用者需要与请求接收者解耦时,命令模式可以使调用者和接收者不直接交互。

2.系统随机请求命令或经常增加、删除命令时,命令模式可以方便的实现这些功能。

3.当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。

4.当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。

4.优缺点分析

优点:

1.通过引入中间件(抽象接口)降低系统的耦合度。

2.扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”。

3.可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。

4.方便实现Undo和Redo操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

5.可以在现有命令的基础上,增加额外功能。比如日志记录,结合装配器模式会更加灵活。

缺点:

1.可能产生大量具体的命令类。因为每一个操作都需要设计一个具体命令类,这会增加系统的复杂性。

2.命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外的类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽离肯定比代码聚合更加难理解。

十六、解释器模式

1.解释

定义:给分析对象定义一种语言,并定义该语言的文法表示,再设计一个解释器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

2.包含的角色

1.抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法interpret()。

2.终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个操作符都有一个具体终结表达式与之相对应。

3.非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。

4.环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。

5.客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

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
//抽象表达式类
interface Expression{
public boolean interpret(String info);
}
//终结符表达式类
class TerminalExpression implements Expression{
private Set<String> set = new HashSet<String>();
public TerminalExpression(String[] data){
for(int i = 0; i < data.length; i++)
set.add(data[i]);
}
public boolean interpret(String info){
if(set.contains(info)){
return true;
}
return false;
}
}
//非终结符表达式
class AndExpression implements Expression{
private Expression city = null;
private Expression person = null;
public AndExpression(Expression city,Expression person){
this.city = city;
this.person = person;
}
public boolean interpret(String info){
String s[] = info.split("的");
return city.interpret(s[0]) && person.interpret(s[1]);
}
}
//环境类
class Context{
private String[] citys = {"韶关","广州"};
private String[] persons = {"老人","妇女","儿童"};
private Expression cityPerson;
public Context(){
Expression city = new TerminalExpression(citys);
Expression person = new TerminalExpression(persons);
cityPerson = new AndExpression(city,person);
}
public void freeRide(String info){
boolean ok = cityPerson.interpret(info);
if(ok)
System.out.println("您是" + info + ",您本次乘车免费!");
else System.out.println(info + ",您不是免费人员,本次乘车扣费2元!")
}
}
//客户端
public class InterpreterPatternDemo{
public static void main(String[] args){
Context bus = new Context();
bus.freeRide("韶关的老人");
bus.freeRide("韶关的年轻人");
bus.freeRide("广州的妇女");
bus.freeRide("广州的儿童");
bus.freeRide("山东的儿童");
}
}
//结果
您是韶关的老人,您本次乘车免费!
韶关的年轻人,您不是免费人员,本次乘车扣费2元!
您是广州的妇女,您本次乘车免费!
您是广州的儿童,您本次乘车免费!
山东的儿童,您不是免费人员,本次乘车扣费2元!

4.优缺点分析

应用场景:

1.当语言的文法较为简单,且执行效率不是关键问题时。

2.当问题重复出现,且可以用一种简单的语言来进行表达时。

3.当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如XML文档解释。

注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。

优点:

1.扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。

2.容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

缺点:

1.执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。

2.会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理和维护。

3.可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。

十七、迭代器模式

1.解释

在现实生活以及程序设计中,经常要访问一个聚合对象中的各个元素,如“数据结构”中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了“开闭原则”。

既然将遍历方法封装在聚合类中不可取,那么聚合类中不提供遍历方法,将遍历方法由用户自己实现是否可行呢?答案是同样不可取,因为这种方式会存在两个缺点:

1.暴露了聚合类的内部表示,使其数据不安全。

2.增加了客户的负担。

“迭代器模式”能较好的克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与其遍历行为,对客户也隐藏了其内部细节,且满足“单一职责原则”和“开闭原则”,比如java中的Collection、List、Set、Map等都包含了迭代器。

迭代器模式在生活中应用的比较广泛,比如:物流系统中的传送带,不管传送什么的是什么物品,都会被打包成一个个箱子,并且有一个统一的二维码。这样我们不需要关心箱子里是什么,在分发时只需要一个个检查发送的目的地即可。

2.包含的角色

1.抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。

2.具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。

3.抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含hasNext()、first()、next()等方法。

4.具体迭代器(ConcreteIterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

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
//抽象聚合
interface Aggregate{
public void add(Object obj);
public void remove(Object obj);
public Iterator getIterator();
}
//具体聚合
class ConcreteAggregate implements Aggregate{
private List<Object> list = new ArrayList<Object>();
public void add(Object obj){
list.add(obj);
}
public void remove(Object obj){
list.remove(obj);
}
public Iterator getIterator(){
return (new ConcreteIterator(list));
}
}
//抽象迭代器
interface Iterator{
Object first();
Object next();
boolean hasNext();
}
//具体迭代器
class ConcreteIterator implements Iterator{
private List<Object> list = null;
private int index = -1;
public ConcreteIterator(List<Object> list){
this.list = list;
}
public boolean hasNext(){
if(index < list.size() -1){
return true;
}else{
return false;
}
}
public Object first(){
index = 0;
Object obj = list.get(index);
return obj;
}
public Object next(){
Object obj = null;
if(this.hasNext()){
obj = list.get(++index);
}
return obj;
}
}
//客户端
public class IteratorPattern{
public static void main(String[] args){
Aggregate ag = new ConcreteAggregate();
ag.add("中山大学");
ag.add("华南理工");
ag.add("韶关学院");
System.out.print("聚合的内容有:");
Iterator it = ag.getIterator();
while(it.hasNext()){
Object obj = it.next();
System.out.print(ob.toString()+"\t");
}
Object ob = it.first();
System.out.println("\nFirst:"+ob.toString());
}
}

应用实现:用迭代器模式编写一个浏览旅游风景图的程序。

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
//代码实现
//实体类
class Spot{
private String name;
private String introduce;
Spot(String name,String introduce){
this.name = name;
this.introduce = introduce;
}
public String getName(){
return this.name;
}
public String getIntroduce(){
return this.introduce;
}
}
//抽象聚合类:景点集接口
interface ViewSpotSet{
void add(Spot obj);
void remove(Spot obj);
ViewSpotIterator getIterator();
}
//具体聚合类:景点集
class MyViewSpotSet implements ViewSpotSet{
private ArrayList<Spot> list = new ArrayList<Spot>();
public void add(Spot obj){
list.add(obj);
}
public void remove(Spot obj){
list.remove(obj);
}
public ViewSpotIterator getIterator(){
return (new MyViewSpotIterator(list));
}
}
//抽象迭代器:景点迭代器接口
interface ViewSpotIterator{
boolean hasNext();
Spot first();
Spot next();
Spot previous();
Spot last();
}
//具体迭代器:景点迭代器
class MyViewSpotIterator implements ViewSpotIterator{
private ArrayList<Spot> list = null;
private int index = -1;
Spot obj = null;
public MyViewSpotIterator(ArrayList<Spot> list){
this.list = list;
}
private boolean hasNext(){
if(index < list.size() - 1){
return true;
}else{
return false;
}
}
public Spot first(){
index = 0;
obj = list.get(index);
return obj;
}
public Spot next(){
if(this.hasNext()){
obj = list.get(index);
}
return obj;
}
public Spot previous(){
if(index > 0){
obj = list.get(--index);
}
return obj;
}
public Spot last(){
index = list.size()-1;
obj = list.get(index);
return obj;
}
}

应用场景:

1.当需要为聚合对象提供多种遍历方式时。

2.当需要为遍历不同的聚合结构提供一个统一的接口时。

3.当访问一个聚合对象的内容而无须暴露其内部实现细节的表示时。

4.优缺点分析

迭代器模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式,其主要优点如下:

1.访问一个聚合对象的内容而无须暴露它的内部表示。

2.遍历任务交由迭代器完成,这简化了聚合类。

3.它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。

4.增加新的聚合类和迭代器类都很方便,无须修改原有代码。

5.封装性良好,为遍历不同的聚合结构提供一个统一的接口。

缺点:

主要缺点是增加了类的个数,这在一定程度上增加了系统的复杂性。

在日常开发中,我们几乎不会自己写迭代器。除非需要定制一个自己实现的数据结构对应的迭代器,否则,开源框架提供的API完全够用。

十八、中介者模式

1.解释

在现实生活中,常常会出现好多对象之间存在复杂的交互关系,这种交互关系常常是”网状结构”,它要求每个对象都必须知道它需要交互的对象。例如,每个人必须记住他所有朋友的电话;而且,朋友中如果有的人电话修改了,他必须让其他所有的朋友一起修改,这叫作”牵一发而动全身”,非常复杂。

如果把这种”网状结构”改为”星型结构”的话,将大大降低它们之间的”耦合性”,这时只要找一个”中介者”就可以了。如前面所说的”每个人必须记住所有朋友电话”的问题,只要在网上建立一个朋友都可以访问的“通信录”就解决了。这样的例子还有很多,例如,你刚刚参加工作想租房,可以找“房屋中介”;或者,自己刚刚到一个陌生城市找工作,可以找“人才交流中心”帮忙。

使用中介者模式,它将大大降低对象之间的耦合性,提高系统的灵活性。

中介者模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立的改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。

2.包含的角色

1.抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。

2.具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个list来管理同事对象,协调各个同事对象之间的交互关系,因此它依赖于同事角色。

3.抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。

4.具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

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
//抽象中介者
abstract class Mediator{
public abstract void register(Colleague colleague);
public abstract void relay(Colleague cl);//转发
}
//具体中介者
class ConcreteMediator extends Mediator{
private List<Colleague> colleagues = new ArrayList<Colleage>();
public void register(Colleague colleague){
if(!colleagues.contains(colleague)){
colleagues.add(colleague);
colleague.setMedium(this);
}
}
public void relay(Colleague cl){
for(Colleague ob:colleagues){
if(!ob.equals(cl)){
((Colleague)ob).receive()
}
}
}
}
//抽象同事类
abstract class Colleague{
protected Mediator mediator;
public void setMedium(Mediator mediator){
this.mediator = mediator;
}
public abstract void receive();
public abstract void send();
}
//具体同事类
class ConcreteColleague1 extends Colleague{
public void receive(){
System.out.println("具体同事1收到请求");
}
public void send(){
System.out.println("具体同事类1发出请求");
mediator.relay(this);//请中介者转发
}
}
//具体同事类
class ConcreteColleague2 extends Colleague{
public void receive(){
System.out.println("具体类同事类2收到请求。");
}
public void send(){
System.out.println("具体同事类2发出请求");
mediator.relay(this);//请中介者转发
}
}
//客户端
public class MediatorPattern{
public static void main(String[] args){
Mediator md = new ConcreteMediator();
Colleague c1,c2;
c1 = new ConcreteColleague1();
c2 = new ConcreteColleague2();
md.register(c1);
md.register(c2);
c1.send();
System.out.println("-----------");
c2.send();
}
}

//运行结果如下:
具体同事类1发出请求。
具体同事类2收到请求。
-------------
具体同事类2发出请求。
具体同事类1收到请求。

应用场景分析:

当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用的时。

当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

4.优缺点分析

优点:

1.类之间各司其职,符合迪米特法则。

2.降低了对象之间的耦合性,使得对象易于独立的被复用。

3.将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。

缺点:

中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变的复杂且难以维护。

十九、备忘录模式

1.解释

每个人都有犯错误的时候,都希望有种“后悔药”能弥补自己的过失,让自己重新开始,但现实很残酷。在计算机应用中,客户同样会常常犯错误,能否提供“后悔药”给他们呢?当然可以!而且是有必要的。这个功能是“备忘录模式”。

备忘录模式能记录一个对象的内部状态,当用户后悔时能撤销当前的操作,使数据恢复到它原先的状态。

备忘录模式的定义:在不破环封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要将该对象恢复到原先保存的状态,该模式又叫快照模式。

2.包含的角色

1.发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其它业务功能,它可以访问备忘录里的所有信息。

2.备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。

3.管理人(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问和修改。

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
//备忘录
class Memento{
private String state;
public Memento(String state){
this.state = state;
}
public void setState(String state){
this.state = state;
}
public String getState(){
return state;
}
}
//发起人
class Originator{
private String state;
public void setState(String state){
this.state = state;
}
public String getState(){
return state;
}
public Memento createMemento(){
return new Memento(state);
}
public void restoreMemento(Memento m){
this.setState(m.getState());
}
}
//管理者
class Caretaker{
private Memento memento;
public void setMemento(Memento m){
memento = m;
}
public Memento getMemento(){
return memento;
}
}
public class MementoPattern{
public static void main(String[] args){
Originator or = new Originator();
Caretaker cr = new Caretaker();
or.setState("S0");
System.out.println("初始状态:"+or.getState());
cr.setMemento(or.createMemento());//保存状态
or.setState("S1");
System.out.println("新的状态:"+or.getState());
or.restoreMemento(cr.getMemento());//恢复状态
System.out.println("恢复状态:"+or.getState());
}
}
//结果打印
初始状态:S0
新的状态:S1
恢复状态:S0

应用场景:
1.需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。

2.需要提供一个可回滚操作的场景,如word、记事本、photoshop,Eclipse等软件在编辑时按ctrl+z组合键,还有数据库中事务操作。

4.优缺点分析

优点:

提供了一种可以恢复状态的机制。当用户需要时能够比较方便的将数据恢复到某个历史的状态。

实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能访问这些状态信息。

简化了发起人类。发起人不需要管理和保护其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,符合单一职责原则。

缺点:

资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。

二十、访问者模式

1.解释

在现实生活中,有些集合对象中存在多种不同的元素,且每种元素也存在多种不同的访问者和处理方式。例如,公园中存在多个景点,也存在多个游客,不同的游客对同一个景色的评价可能不同;医院医生开的处方单中包含多种药元素,查看它的划价员和药房工作人员对它的处理方式也不同,划价员根据处方单上面的药品名和数量进行划价,药房工作人员根据处方单的内容进行抓药。

这些被处理的数据元素相对稳定而访问方式多种多样的数据结构,如果用“访问者模式”来处理比较方便。访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,且不用修改原来的程序结构与数据结构,这提高了程序的扩展性和灵活性。

访问者模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。他将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。

2.包含的角色

1.抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体类对应一个访问操作visit(),该操作中的参数类型标识了被访问的具体元素。

2.具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。

3.抽象元素(Element)角色:声明一个包含接受操作accept()的接口,被接受的访问者对象作为accept()方法的参数。

4.具体元素(ConcreteElement)角色:实现抽象元素本身角色提供的accept()操作,其方法体通常都是visitor.visit(this),另外具体元素中可能还包含本身业务逻辑的相关操作。

5.对象结构(ObjectStructure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由List、Set、Map等聚合类实现。

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
//抽象访问者
interface Visitor{
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
//具体访问者A类
class ConcreteVisitorA implements Visitor{
public void visit(ConcreteElementA element){
System.out.println("具体访问者A访问-->" + element.operationA());
}
public void visit(ConcreteElmentB element){
System.out.println("具体访问者A访问-->" + element.operationB());
}
}
//具体访问者B类
class ConcreteVisitorB implements Visitor{
public void visit(ConcreteElementA element){
System.out.println("具体访问者B访问-->" + element.operationA());
}
public void visit(ConcreteElmentB element){
System.out.println("具体访问者B访问-->" + element.operationB());
}
}
//抽象元素类
interface Element{
void accept(Visitor visitor);
}
//具体元素A类
class ConcreteElementA implements Element{
public void accept(Visitor visitor){
visitor.visit(this);
}
public String operationA(){
return "具体元素A的操作"
}
}
//具体元素B类
class ConcreteElementB implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationB() {
return "具体元素B的操作。";
}
}
//对象结构角色
class ObjectStructure {
private List<Element> list = new ArrayList<Element>();
public void accept(Visitor visitor) {
Iterator<Element> i = list.iterator();
while (i.hasNext()) {
((Element) i.next()).accept(visitor);
}
}
public void add(Element element) {
list.add(element);
}
public void remove(Element element) {
list.remove(element);
}
}
//客户端
public class VisitorPattern {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
os.add(new ConcreteElementA());
os.add(new ConcreteElementB());
Visitor visitor = new ConcreteVisitorA();
os.accept(visitor);
System.out.println("------------------------");
visitor = new ConcreteVisitorB();
os.accept(visitor);
}
}

4.优缺点分析

优点:

1)扩展性好,能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。

2)复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。

3)灵活性好。访问者模式将数据结构与作用与结构上的对象解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。

4)符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使得每个访问者的功能都比较单一。

缺点:

1)添加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问类中增加相应的具体操作,这违背了“开闭原则”。

2)破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。

3)违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。

二十一、模板方法模式

1.解释

在面向对象程序设计过程中,程序员常常会遇到这样的情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。

例如:一个人每天会起床、吃饭、做事、睡觉等,其中“做事”的内容每天可能不同。我们把这些规定了流程或格式的实例定义成模板,允许使用者根据需求去更新它,例如,简历模板、论问模板、Word中的模板文件等。

模板方法模式定义:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。

2.包含的角色

1.抽象类/抽象模板(Abstract Class)

抽象模板类,负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下。

1)模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

2)基本方法:是整个算法中的一个步骤,包含以下几种类型。

抽象方法:在抽象类中声明,由具体子类实现。

具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。

钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

2.具体子类/具体实现(Concrete Class)

具体实现类,实现抽象类中所定义的抽象方法和钩子方法,他们是一个顶级逻辑的一个组成步骤。

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
//抽象类
abstract class AbstractClass{
//模板方法
public void TemplateMethod(){
SpecificMethod();
abstractMethod1();
abstractMethod2();
}
//具体方法
public void SpecificMethod(){
System.out.println("抽象类中的具体方法调用...");
}
//抽象方法1
public abstract void abstractMethod1();
//抽象方法2
public abstract void abstractMethod2();
}
//具体子类
class ConcreteClass extends AbstractClass{
public void abstractMethod1(){
System.out.println("抽象方法1的实现被调用...");
}
public void abstractMethod2(){
System.out.println("抽象方法2的实现被调用...");
}
}
//客户端
public class TemplateMethodPattern{
public static void main(String[] args){
AbstractClass tm = new ConcreteClass();
tm.TemplateMethod();
}
}
//运行结果如图
抽象类中的具体方法被调用...
抽象方法1的实现被调用...
抽象方法2的实现被调用...

应用场景:

1)算法的整体步骤很固定,但其中个别部分易变的时候,这时候可以使用模板方法模式,将容易变得部分抽象出来,供子类实现。

2)当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。

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
//含钩子方法
//抽象类
abstract class HookAbstractClass{
//模板方法
public void TemplateMethod(){
abstractMethod1();
hookMethod1();
if(hookMethod2()){
specificMethod();
}
abstractMethod2();
}
//具体方法
public void specificMethod(){
System.out.println("抽象类中的具体方法被调用...");
}
//钩子方法1
public void hookMethod1(){
}
//钩子方法2
public void hookMethod2(){
return true;
}
//抽象方法1
public abstract void abstractMethod1();
//抽象方法2
public abstract void abstractMethod2();
}
//具体子类
class HookConcreteClass extends HookAbstractClass{
public void abstractMethod1(){
System.out.println("抽象方法1的实现被调用...");
}
public void abstractMethod2(){
System.out.println("抽象方法2的实现被调用...");
}
public void hookMethod1(){
System.out.println("钩子方法1被重写...");
}
public boolean hookMethod2(){
return false;
}
}
//客户端
public class HookTemplateMethod{
public static void main(String[] args){
HookAbstractClass tm = new HookAbstractClass();
tm.TemplateMethod();
}
}
//运行结果
抽象方法1的实现被调用...
钩子方法1被重写...
抽象方法2的实现被调用...

4.优缺点分析

优点:

1)封装了不变的部分,扩展了可变的部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。

2)它在父类中提取了公共的部分代码,便于代码复用。

3)部分方法是由子类实现的,因此子类可以通过扩展方法增加相应的功能,符合开闭原则。

缺点:
1)对每个不同的实现都需要定义一个子类,这会导致类的个数增大,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。

2)父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

3)由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有的子类都要改一遍。

二十二、策略模式

1.解释

在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以采用打折、送商品、送积分等方法。

在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。

如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改源代码,不易维护,违背开闭原则。如果采用策略模式就能很好的解决该问题。

策略模式定义:该模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

2.包含的角色

(1)抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。

(2)具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。

(3)环境(Context)类:持有一个策略类的引用,最终给客户端调用。

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
//抽象策略类
interface Strategy{
public void strategyMethod();//策略方法
}
//具体策略类A
class ConcreteStrategyA implements Strategy{
public void strategyMethod(){
System.out.println("具体策略A的策略方法被访问!");
}
}
//具体策略类B
class ConcreteStrategyB implements Strategy{
public void strategyMethod(){
System.out.println("具体策略B的策略方法被访问!");
}
}
//环境类
class Context{
private Strategy strategy;
public Strategy getStrategy(){
return strategy;
}
public void setStrategy(Strategy strategy){
this.strategy = strategy;
}
public void strategyMethod(){
strategy.strategyMethod();
}
}
//客户端
public class StrategyPattern{
public static void main(String[] args){
Context c = new Context();
Strategy s = new ConcreteStrategyA();
c.setStrategy(s);
c.strategyMethod();
System.out.println("----------");
s = new ConcreteStrategyB();
c.setStrategy(s);
c.strategyMethod();
}
}
//运行结果
具体策略A的策略方法被访问!
-----------------
具体策略B的策略方法被访问!

应用场景分析:

1.一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。

2.一个类中定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。

3.系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。

4.系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。

5.多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

扩展:

在一个使用策略模式的系统中,当存在的策略很多时,客户端管理所有策略算法将变的很复杂,如果在环境类中使用策略工厂模式来管理这些策略类将大大的减少客户端的工作复杂度。

4.优缺点分析

优点:

1.多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如if…else语句、switch…case语句。

2.策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。

3.策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。

4.策略模式提供了对开闭原则的完美支持,可以在不修改源代码的情况下,灵活的增加算法。

5.策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

缺点:

1.客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。

2.策略模式造成很多的策略类,增加维护难度。

二十三、状态模式

1.解释

在软件开发过程中,应用程序中的部分对象可能会根据不同情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态就会发生变化。如人都有高兴和伤心的时候,不同的情绪有不同的行为,当然外界也会影响其情绪变化。

对这种有状态的对象编程,传统的解决方案是:将这些所有可能发生的情况全都考虑到,然后使用if-else或switch-case语句来做状态判断,再进行不同的情况处理。但是显然这种做法对复杂的状态判断存在天然弊端,条件判断语句会过于臃肿,可读性差,且不具备扩展性,维护难度也大。且增加新的状态时要添加新的if-else语句,这违背了”开闭原则“,不利于程序的扩展。

以上问题如果采用”状态模式“就能很好的得到解决。状态模式的解决思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关”判断逻辑“提取出来,用各个不同的类进行表示,系统处于哪种情况,直接使用相应的状态类对象进行处理,这样能把原来复杂的逻辑判断简单化,消除了if-else、switch-case等冗余语句,代码更有层次性,并且具备良好的扩展力。

状态模式的定义:对有状态的对象,把复杂的”判断逻辑“提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

2.包含的角色

(1)环境类(Context)角色:也称上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。

(2)抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。

(3)具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

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
//环境类
class Context{
private State state;
//定义环境类的初始状态
public Context(){
this.state = new ConcreteStateA();
}
//设置新状态
public void setState(State state){
this.state = state;
}
//读取状态
public State getState(){
return (state);
}
//对请求做处理
public void Handle(){
state.Handle(this);
}
}
//抽象状态类
abstract class State{
public abstract void Handle(Context context);
}
//具体状态A类
class ConcreteStateA extends State{
public void Handle(Context context){
System.out.println("当前状态是A");
context.setState(new ConcreteStateB());
}
}
//具体状态B类
class ConcreteStateB extends State{
public void Handle(Context context){
System.out.println("当前状态是B");
context.setState(new ConcreteStateA());
}
}
//客户端
public class StatePatternClient{
public static void main(String[] args){
Context context = new Context();//创建环境
context.Handle();//处理请求
context.Handle();
context.Handle();
context.Handle();
}
}
//运行结果
当前状态是 A.
当前状态是 B.
当前状态是 A.
当前状态是 B.

应用场景:

1.当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。

2.一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

扩展:

在有些情况下,可能有多个环境对象需要共享一组状态,这时需要引入享元模式,将这些具体状态对象放在集合中供程序共享。

4.优缺点分析

优点:

1.结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足”单一职责原则“。

2.将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。

3.状态类职责明确,有利于程序的扩展。通过定义新的子类很容易的增加新的状态和转换。

缺点:

1.状态模式的使用必然会增加系统的类与对象的个数。

2.状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。

3.状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。

5.拓展

1.状态模式与责任链模式的区别

状态模式和责任链模式都能消除if-else分支过多的问题。但在某些情况下,状态模式中的状态可以理解为责任,那么在这种情况下,两种模式都可以使用。

从定义来看,状态模式强调的是一个对象内在状态的改变,而责任链强调的是外部节点对象间的改变。

从代码实现上来看,两者最大的区别就是状态模式的各个状态对象知道自己要进入的下一个状态对象,而责任链不知道其下一个节点处理对象,因为链式组装由客户端负责。

2.状态模式和策略模式的区别

状态模式和策略模式的UML类图架构几乎完全一样,但两者的应用场景是不一样的。策略模式的多种算法行为择其一都能满足,彼此之间是独立的,用户可自行更换策略算法,而状态模式的各个状态间存在相互关系,彼此之间在一定条件下存在自动切换状态的效果,并且用户无法指定状态,只能设置初始状态。

二十四、面向对象设计原则

1.开闭原则

软件实体应当对扩展开放,对修改关闭,这就是开闭原则的经典定义。这里的软件实体包括以下几个部分:1.项目中划分的模块 2.类与接口 3.方法

开闭原则的含义是:当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。

开闭原则是面向对象程序设计的终极目标,它使软件实体拥有一定的适应性和灵活性的同时具备稳定性和延续性。作用如下:

1.对软件测试的影响

2.可以提高代码的可复用性

3.可以提高软件的可维护性

2.里氏替换原则

继承必须确保超类所拥有的性质在子类中仍然成立。里氏替换原则是继承复用的基础,它反映了基类和子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。

作用:

1.里氏替换原则是实现开闭原则的重要方式之一。

2.它克服了继承中重写父类造成的可复用性变差的缺点。

3.它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。

4.加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险。

里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

里氏替换原则的定义:

1.子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

2.子类中可以增加自己特有的方法。

3.当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松。

4.当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类的方法更严格或相等。

3.依赖倒置原则

原始定义为:高层模块不应该依赖底层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。其核心思想是:要面向接口编程,不要面向实现编程。

依赖倒置原则是实现开闭原则的重要途经之一,它降低了客户和实现模块之间的耦合。

这里的抽象指的是接口或者抽象类,而细节是指具体的实现类。

使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成。

依赖倒置原则的作用:

1.依赖倒置原则可以降低类间的耦合性

2.依赖倒置原则可以提高系统的稳定性

3.依赖倒置原则可以减少并行开发引起的风险

4.依赖倒置原则可以提高代码的可读性和可维护性

实现方法:
1.每个类尽量提供接口或抽象类,或者两者都具备。

2.变量的声明类型尽量是接口或者抽象类

3.任何类都不应该从具体类派生

4.使用继承时尽量遵循里氏替换原则。

4.单一职责原则

又称单一功能原则,这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。

该原则提出对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点:

1.一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力

2.当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。

优点:

单一职责原则的核心就是控制类的粒度大小,将对象解耦、提高内聚性。如果遵循单一职责原则将有以下优点。

降低类的复杂度。一个类只负责一项职责,其逻辑肯定比要负责多项职责简单的多。

提高类的可读性,复杂性降低,自然其可读性会提高

提高系统的可维护性,可读性提高了,那自然就更容易维护了。

变更引起的风险降低。变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。

5.接口隔离原则

接口隔离原则要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。

定义是:客户端不应该被迫依赖于它不使用的方法。该原则还有另外一个定义:一个类对另一个类的依赖应该建立在最小的接口上。

以上两个定义的含义是:要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

接口隔离原则和单一职责原则都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:
单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离

单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

6.迪米特法则

迪米特法则的定义是只与你的直接朋友交谈,不跟陌生人说话。其含义是如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类间的耦合度,提高模块的相对独立性。

迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。

优点:

迪米特法则则要求限制软件实体之间的通信的宽度和深度,正确使用迪米特法则将有以下两个优点:

降低了类之间的耦合度,提高了模块的相对独立性。

由于亲和度降低,从而提高了类的可复用率和系统的扩展性。

但是,过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在采用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

  • Post title:设计模式
  • Post author:虫
  • Create time:2021-10-18 11:25:31
  • Post link:https://redefine.ohevan.com/2021/10/18/设计模式/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
On this page
设计模式