首页 设计模式学习
文章
取消

设计模式学习

从作业部落迁移过来

image

讲解前言

1、学习的目的

  • OO原则是我们的目标,而设计模式是我们的做法!
  • 设计模式的价值:使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 https://www.jianshu.com/p/8e5d471b1d9f

2、面向对象(OO)的三大特性五大原则

(1)三大特性:封装,继承,多态
  • 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。封装是面向对象的特征之一,是对象和类概念的主要特性。 简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
  • 继承,是指可以让某个类型的对象获得另一个类型的对象的属性的方法。它支持按级分类的概念。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
  • 多态,就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。
(2)五大基本原则
1> 单一职责原则SRP(Single Responsibility Principle)

是指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。

2> 开放封闭原则OCP(Open-Close Principle)

一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能, 那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。

3> 替换原则(the Liskov Substitution Principle LSP)

子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工, 也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。 比如:父类求和方法,在子类中不再是求和功能。属错误情况。

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
//  场景一
class A{
        public int func1(int a, int b){
            return a-b;
        }
    }

    public class Client{
        public static void main(String[] args){
            A a = new A();
            System.out.println("100-50="+a.func1(100, 50));
            System.out.println("100-80="+a.func1(100, 80));
        }
    }
}

/**
*   新需求:完成两数相加,然后再与100求和
*/

//  错误实现
class B extends A{
        public int func1(int a, int b){
            return a+b;
        }

        public int func2(int a, int b){
            return func1(a,b)+100;
        }
    }

    public class Client{
        public static void main(String[] args){
            B b = new B();
            System.out.println("100-50="+b.func1(100, 50));
            System.out.println("100-80="+b.func1(100, 80));
            System.out.println("100+20+100="+b.func2(100, 20));
        }
    }
}

总结子类可以扩展父类的功能但不能改变父类原有的功能
4> 依赖原则(the Dependency Inversion Principle DIP) 具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,

这个时候,B不应当直接使用A中的具体类: 而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口:这样就达到 了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造成循环依赖。一个常见的问题就是编译A模块时需要直接包含到B模块的cpp文件,而编译B时同样要直接包含到A的cpp文件。

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
//  正常情况下
class Book{
    public String getContent(){
        return "很久很久以前有一个阿拉伯的故事……";
    }
}
 
class Mother{
    public void narrate(Book book){
        System.out.println("妈妈开始讲故事");
        System.out.println(book.getContent());
    }
}
 
public class Client{
    public static void main(String[] args){
        Mother mother = new Mother();
        mother.narrate(new Book());
    }
}

/**
*   新需求,读其他类型杂志????
*/
//  拓展后
interface IReader{
    public String getContent();
}

class Newspaper implements IReader {
    public String getContent(){
        return "林书豪17+9助尼克斯击败老鹰……";
    }
}
class Book implements IReader{
    public String getContent(){
        return "很久很久以前有一个阿拉伯的故事……";
    }
}
 
class Mother{
    public void narrate(IReader reader){
        System.out.println("妈妈开始讲故事");
        System.out.println(reader.getContent());
    }
}
 
public class Client{
    public static void main(String[] args){
        Mother mother = new Mother();
        mother.narrate(new Book());
        mother.narrate(new Newspaper());
    }
}

image

5> 接口分离原则(the Interface Segregation Principle ISP)

模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来

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
//  错误例子

interface I {
    public void method1();
    public void method2();
    public void method3();
    public void method4();
    public void method5();
}
 
class A{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method2();
    }
    public void depend3(I i){
        i.method3();
    }
}
 
class B implements I{
    public void method1() {
        System.out.println("类B实现接口I的方法1");
    }
    public void method2() {
        System.out.println("类B实现接口I的方法2");
    }
    public void method3() {
        System.out.println("类B实现接口I的方法3");
    }
    //对于类B来说,method4和method5不是必需的,但是由于接口A中有这两个方法,
    //所以在实现过程中即使这两个方法的方法体为空,也要将这两个没有作用的方法进行实现。
    public void method4() {}
    public void method5() {}
}
 
class C{
    public void depend1(I i){
        i.method1();
    }
    public void depend2(I i){
        i.method4();
    }
    public void depend3(I i){
        i.method5();
    }
}
 
class D implements I{
    public void method1() {
        System.out.println("类D实现接口I的方法1");
    }
    //对于类D来说,method2和method3不是必需的,但是由于接口A中有这两个方法,
    //所以在实现过程中即使这两个方法的方法体为空,也要将这两个没有作用的方法进行实现。
    public void method2() {}
    public void method3() {}
 
    public void method4() {
        System.out.println("类D实现接口I的方法4");
    }
    public void method5() {
        System.out.println("类D实现接口I的方法5");
    }
}
 
//  对接口I进行拆分后:
interface I1 {
    public void method1();
}
 
interface I2 {
    public void method2();
    public void method3();
}
 
interface I3 {
    public void method4();
    public void method5();
}
 
class A{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I2 i){
        i.method2();
    }
    public void depend3(I2 i){
        i.method3();
    }
}
 
class B implements I1, I2{
    public void method1() {
        System.out.println("类B实现接口I1的方法1");
    }
    public void method2() {
        System.out.println("类B实现接口I2的方法2");
    }
    public void method3() {
        System.out.println("类B实现接口I2的方法3");
    }
}
 
class C{
    public void depend1(I1 i){
        i.method1();
    }
    public void depend2(I3 i){
        i.method4();
    }
    public void depend3(I3 i){
        i.method5();
    }
}
 
class D implements I1, I3{
    public void method1() {
        System.out.println("类D实现接口I1的方法1");
    }
    public void method4() {
        System.out.println("类D实现接口I3的方法4");
    }
    public void method5() {
        System.out.println("类D实现接口I3的方法5");
    }
}

2、全书目录章节

概览 引子 1 设计模式入门 欢迎来到设计模式世界 2 观察者模式 让你的对象知悉现况 3 装饰者模式 装饰对象 4 工厂模式 烘烤OO的精华 5 单件模式 独一无二的对象 6 命令模式 封装调用 7 适配器模式与外观模式 8 模板方法模式 封装算法 9 选代器与组合模式 管理良好的集合 10 状态模式 事物的状态 11 代理模式 控制对象访问 12 复合模式 模式中的模型 13 与设计模式相处 真实世界中的模式 附录A:剩下的模式

一、设计模式入门 欢迎来到设计模式世界

面向对象(OO)的“duck”

基类

  • “会嘎嘎叫”:quack()
  • “会游泳”:swim()
  • “外观”:display()

(1)场景一:不同外观的鸭子

绿头鸭:继承基类并重载display() 红头鸭:继承基类并重载display()

(2)场景二:会飞的鸭子

需求:有些鸭子会飞

1> 基类中新增:fly()
  • “会呱呱叫”:quack()
  • “会游泳”:swim()
  • “外观”:display()
  • “会飞”:fly()

解决了当前的鸭子会飞的问题。

2> 意外出现…纳尼?
  • 唐老鸭会飞了?
  • 橡皮鸭会飞了?

简单的fly(),导致了所有的鸭子会飞; 对基类局部的修改,影响是全局的。

体会:将fly()添加到基类,但在不会飞的鸭子中,重载fly()可解决使fly()无效。方便了会飞的鸭子reuse fly(),导致不会飞鸭子Overload

##(3)场景三:所有鸭子都会叫? 会嘎嘎叫 会呱呱叫 …

当新的鸭子出现时,需检查:会不会叫 不能保证所有的鸭子都会叫

思考:考虑让某些鸭子会某些function

##(4)设计原则:把问题归零

1> 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
  • 做法:把会变化的部分取出来,并“封装”起来,好让其他部分不会受到影响
  • 结果:代码变化引起的不经意后果变少,系统变得更有弹性
2> 分开变化和不会变化的部分(以duck为例)

会变的方法:

  • fly()
  • quack()

取出fly封装成“飞行类”,取出quack封装成“呱呱叫类”

3> 设计原则:针对接口编程,不针对实现编程

分析,动物会发出声音,但是会发出不同的声音。 不变化是:发出声音 变化的是:不同的声音

Animal基类有方法:发出声音:makeSound() Dog继承Animal,但makeSound()实现”汪汪叫:bark()” Cat继承Animal,但makeSound()实现”喵喵叫:meow()”

因此,

1
2
3
4
5
Animal animal1 = new Dog();
    animal1.makeSound();    //  汪汪叫
    
Animal animal2 = new Cat();
    animal2.makeSound();    //  喵喵叫

##(5)实现鸭子“会飞”、“会叫”的新方式 image

##(6)设计原则:多用组合少用继承 image

##(7)探索尝试:鸭鸣器 猎人使用“鸭鸣器”模拟鸭叫声,引诱野鸭。

如何实现鸭鸣器呢?(借鉴“组合”)

总结:策略模式(Strategy Pattern)

定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。 image

二、观察者模式 让你的对象知悉现况

  • 需求如下: image

  • 显示装置: image

  • WeatherData类:

    • getTemperature()
    • getHumidity()
    • getPressure()
    • measuremmentsChanged()

1、初步解决方案(方案一)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class WatherData {
    public void measurementsChanged {
        float temp = getTemperature()
        float humidity = getHumidity()
        float pressure = getPressure()
        //  更新 目前状况
        currentConditionsDisplay.update(temp, humidity, pressure)
        //  更新 天气统计
        statisticsDisplay.update(temp, humidity, pressure)
        //  更新 天气预报
        forecastDisplay.update(temp, humidity, pressure)
    }
}

分析问题: 1、更新了三次,若有第四次,第五次,第n次等,则需修改代码。(不易拓展) 2、几次更新都一样,是否可以合并一下呢?

2、观察者模式概念介绍

描述:“出版者Subject” + “订阅者Observer” = 观察者模式 定义:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者(观察者)都会受到通知(并自动更新)

3、观察者模式下的解决(方案二)

image

4、设计原则:为了交互对象之间松耦合而努力

(1)松耦合的设计:通过使对象之间的互相依赖将到最低,来让我们建立有弹性的OO系统,以不变应万变。 (2)松耦合展示草图: image

5、java观察者内部类:次序问题

三、装饰者模式 装饰对象

需求: image

1、初步解决方案(方案一)

image

分析:违反:(1)把问题归零。()针对接口编程,不针对实现编程

2、改进

Beverage中的cost计算出各种调料的价钱,子类中,调用父类cost后,再加上指定类型的饮料类型的价钱

分析不足:

  • 调料价钱改变时,需要改变代码
  • 出现新的调料,需添加新的方法
  • 新的饮料可能会不适用当前的cost
  • 顾客买两份饮料,怎么办?

3、设计原则:类应该对拓展开发,对修改关闭

允许类容易拓展,在不修改现有代码的情况下,就可搭配新的行为。

4、装饰模式使用场景:

动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。不改变接口的前提下,增强所考虑的类的性能。

  • 需要扩展一个类的功能,或给一个类增加附加责任。
  • 需要动态的给一个对象增加功能,这些功能可以再动态地撤销。
  • 需要增加一些基本功能的排列组合而产生的非常大量的功能,从而使继承变得 不现实。

5、使用装饰模式实现

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 Beverage {
    public String getDescription();
    public double cost();
}

//  装饰类
public class Decorator implements Beverage {
    private String description = "我只是装饰器,不知道具体的描述";
    @Override
    public String getDescription() {
        return description;
    }
    @Override
    public double cost() {
        return 0;        //价格由子类来决定
    }
}

//  摩卡咖啡(50元)
public class Mocha implements Beverage {
    private String description = "摩卡咖啡
    @Override
    public String getDescription() {
        return description;
    }
    @Override
    public double cost() {
        return 50;
    }
}

//  调料->牛奶(加牛奶则加20)
public class Milk extends Decorator {
    private String description = "加了牛奶";
    private Beverage beverage = null;
    public Milk(Beverage beverage){
        this.beverage = beverage;
    }
    public String getDescription(){
        return beverage.getDescription()+"\n"+description;
    }
    public double cost(){
        return beverage.cost()+20;    //20表示牛奶的价格
    }
}

    //  测试下:摩卡+牛奶(牛奶装饰摩卡)
    Beverage beverage = new Mocha();
    beverage = new Milk(beverage);
    beverage.cost()
    // 50 + 20 = 70

image

四、工厂模式 烘烤OO的精华

1
2
3
4
5
6
7
8
9
10
11
12
13
//  当有具体类MallardDuck时,实例化:
Pizza pizza = new CheesePizza()

// 当有一堆具体类可选时,实例化
if (picnic) {
    pizza = new MallardDuck()
}else if (hunting) {
    pizza = new PepperoniPizza()
}else if (inBathTub) {
    pizza = new ClamPizza()
}

//  当新增一种具体类或者减少一种具体类时,如何处理

披萨增加或者减少 image

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
//  简单pizza工厂
class SimplePizzaFactory{
    public Pizza createPizza(String type){
        Pizza pizza=null;
        if(type.equals("cheese")){
               pizza=new CheesePizza();
        }else if(type.equals("pepperoni")){
               pizza=new PepperoniPizza();
        }else if(type.equals("clam")){
               pizza=new ClamPizza();
        }else if(type.equals("veggie")){
               pizza=new VeggiePizza();
        }
     return pizza; 
    }
}

//  Pizza商店
class PizzaStore{
    SimplePizzaFactory factory;
    public PizzaStore(SimplePizzaFactory factory){
            this.factory=factory;
    }
    public Pizza orderPizza(String type){
        Pizza pizza;

        pizza=factory.createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

分析好处:不止给PizzaStore用还给其他人用

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
//  将createPizza放回PizzaStore
class NYPizzaStore extends PizzaStore{
    Pizza createPizza(String type){
         if(type.equals("cheese")){
               pizza=new NYStyleCheesePizza();
        }else if(type.equals("pepperoni")){
               pizza=new NYStylePepperoniPizza();
        }else if(type.equals("clam")){
               pizza=new NYStyleClamPizza();
        }else if(type.equals("veggie")){
               pizza=new NYStyleVeggiePizza();
        }
    }
}

//  抽象披萨类
abstract class Pizza{
    String name;
    String dough;
    String sauce;
    ArrayList toppings =new ArrayList();

    void prepare(){
        System.out.println("Preparing"+name);
        System.out.println("Tossing dough");
        System.out.println("Adding sauce");
        System.out.println("Adding toppings:");
        for(int i=0;i<toppings.size();i++){
            System.out.println(""+toppings.get(i));
        }
    }
    void bake(){
        System.out.println("Bake for 25 minutes at 350");
    }
    void cut(){
        System.out.println("Cutting the pizza into diagonal slices");
    }
    void box(){
        System.out.println("Place pizza in official PizzaStore box");
    }
    public String getName(){
        return name;
    }
}

//  芝士披萨(ny)
class NYStyleCheesePizza extends Pizza{
    public NYStyleCheesePizza(){
        name="NY Style Sauce and Cheese Pizza";
        dough="Thin Crust Dough";
        sauce="Marinara Sauce";
        toppings.add("Grated Reggiano Cheese");
    }
}

//  芝士披萨(Chaicago)
class ChicagoStyleCheesePizza extends  Pizza{
    public ChicagoStyleCheesePizza(){
        name="Chicago Style Deep Dish Cheese Pizza";
        dough="Extra Thick Crust Dough";
        sauce="Plum Tomato Sauce";
        toppings.add("Shredded Mozzarella Cheese");
    }

    @Override
    void cut() {
        System.out.println("Cutting the pizza into square slices");
    }
}

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
98
99
interface PizzaIngredientFactory{
    public Dough createDough();
    public Sauce createSauce();
    public Cheese createCheese();
    public Veggies[] createVeggies();
    public Pepperoni createPepperoni();
    public Clams createClams();
}

//  ny原料工厂
class NYPizzaIngredientFactory implements PizzaIngredientFactory{

    @Override
    public Dough createDough() {
        return null;
    }

    @Override
    public Sauce createSauce() {
        return null;
    }

    @Override
    public Cheese createCheese() {
        return null;
    }

    @Override
    public Veggies[] createVeggies() {
        return new Veggies[0];
    }

    @Override
    public Pepperoni createPepperoni() {
        return null;
    }

    @Override
    public Clams createClams() {
        return null;
    }
}

//  pizza抽象类
abstract class Pizza{
    String name;
    Dough dough;
    Sauce sauce;
    Veggies veggies[];
    Cheese cheese;
    Pepperoni pepperoni;
    Clams clams;
    abstract void prepare();
    void cut(){
        System.out.println("Cutting the pizza into diagonal slices");
    }
    void box(){
        System.out.println("Place pizza in official PizzaStore box");
    }
    void setName(String name){
        this.name=name;
    }
    public String getName(){
        return name;
    }
    public void toString2(){
        //这里打印披萨的代码
    }
}

//  芝士披萨
class CheesePizza extends Pizza{
    PizzaIngredientFactory ingredientFactory;
    public CheesePizza(PizzaIngredientFactory ingredientFactory){
        this.ingredientFactory=ingredientFactory;
    }
    @Override
    void prepare() {
        System.out.println("Preparing"+name);
        dough=ingredientFactory.createDough();
        sauce=ingredientFactory.createSauce();
        cheese=ingredientFactory.createCheese();
    }
}

//  ny披萨店
class NYPizzaStore extends PizzaStore{
   protected Pizza createPizza(String item){
       Pizza pizza=null;
       PizzaIngredientFactory ingredientFactory=new NYPizzaIngredientFactory();
       if(item.equals("cheese")){
           pizza=new CheesePizza(ingredientFactory);
           pizza.setName("New ……");
       }else if(item.equals("veggie")){
           //……
       }
       return pizza;
   }
}

image

五、单件模式

1、定义:确保一个类只有一个实例,并提供一个全局的访问点

2、实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class SingleObject {
   //创建 SingleObject 的一个对象
   private static SingleObject instance = new SingleObject();
 
   //让构造函数为 private,这样该类就不会被实例化
   private SingleObject(){}
 
   //获取唯一可用的对象
   public static SingleObject getInstance(){
      return instance;
   }
 
   public void showMessage(){
      System.out.println("Hello World!");
   }
}

3、实现方式

java:http://www.runoob.com/design-pattern/singleton-pattern.html

// oc

#import <Foundation/Foundation.h>
@interface Singleton: NSObject
+(instancetype) shareInstance;
@end
#import "Singleton.h"
@implementation Singleton
static Singleton* _instance = nil;
+(instancetype) shareInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}
@end

// swift

1
2
3
4
5
6
class MyManager {
    static let shared = MyManager()
}

let shared = MyManager()
class MyManager { }

六、命令模式 封装调用

1、定义

命令模式(Command Pattern):将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

2、结构

Command: 抽象命令类

  • ConcreteCommand: 具体命令类
  • Invoker: 调用者
  • Receiver: 接收者
  • Client:客户类

3、遥控器与电视机

4、分析

命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。

每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。 命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。 命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。 命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

5、优缺点

优点

  • 降低系统的耦合度。
  • 新的命令可以很容易地加入到系统中。
  • 可以比较容易地设计一个命令队列和宏命令(组合命令)。
  • 可以方便地实现对请求的Undo和Redo。

缺点

  • 使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。

6、应用场景

系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。 系统需要在不同的时间指定请求、将请求排队和执行请求。 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。 系统需要将一组操作组合在一起,即支持宏命令

七、适配器模式与外观模式

引入适配器模式 image

1、适配器定义:定义一个包装类,用于包装不兼容接口的对象

作用:把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法一起工作的两个类能够在一起工作。

2、适配器场景

(1)需求:进口电视机要求电压(110V)与国内插头标准输出电压(220V)不兼容
(2)解决方案:设置一个适配器将插头输出的220V转变成110V
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
// 创建源类(原有的插头)
class PowerPort220V{
    //原有插头只能输出220V
    public void Output_220v(){
    
    }
}

// Target接口(期待得到的插头)
public interface Target {
    //将220V转换输出110V(原有插头(Adaptee)没有的)
    public void Convert_110v();
}

// 创建适配器类(Adapter)
class Adapter220V extends PowerPort220V implements Target{
    @Override
    public void Convert_110v(){
      this.Output_220v;
    }
}

///  使用目标类
//  进口机器类
class ImportedMachine {
    @Override
    public void Work() {
        System.out.println("进口机器正常运行");
    }
}

//  通过Adapter类从而调用所需要的方法
public class AdapterPattern {
    public static void main(String[] args){
        Target mAdapter220V = new Adapter220V();
        ImportedMachine mImportedMachine = new ImportedMachine();
        mAdapter220V.Convert_110v();
        mImportedMachine.Work();
    }
}

3、外观模式

外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。

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
65
66
67
68
69
//灯类
public class SubSystemA_Light {  
     public void on(){  
        System.out.println("打开了灯....");  
    }  
      
     public void off(){  
        System.out.println("关闭了灯....");  
    }  
}  

//电视类
public class SubSystemB_Television {  
     public void on(){  
        System.out.println("打开了电视....");  
    }  
      
     public void off(){  
        System.out.println("关闭了电视....");  
    }  
}  

//空调类
public class SubSystemC_Aircondition {  
     public void on(){  
        System.out.println("打开了电视....");  
    }  
      
     public void off(){  
        System.out.println("关闭了电视....");  
    }  
}  

// 客户端调用
public class Facade Pattern{ 
      public static void main(String[] args){
            SubSystemA_Light light = new SubSystemA_Light();
            SubSystemB_Television television = new SubSystemB_Television();
            SubSystemC_Aircondition aircondition = new SubSystemC_Aircondition();

            //起床后开电器
            System.out.prinln("起床了")
            light.on()
            television.on();
            aircondition.on()
            System.out.prinln("可以看电视了")

           //睡觉时关电器
            System.out.prinln("睡觉了")
            light.off()
            television.off();
            aircondition.off()
            System.out.prinln("可以睡觉了")
        }
    }
}

//  客户端调用结果
起床了
打开了灯
打开了电视
打开了空调
可以看电视了

睡觉了
关闭了灯
关闭了电视
关闭了空调
可以睡觉了

5、外观模式与适配器模式比较

外观模式的实现核心主要是——由外观类去保存各个子系统的引用,实现由一个统一的外观类去包装多个子系统类,然而客户端只需要引用这个外观类,然后由外观类来调用各个子系统中的方法。 这样的实现方式非常类似适配器模式,然而外观模式与适配器模式不同的是:适配器模式是将一个对象包装起来以改变其接口,而外观是将一群对象 ”包装“起来以简化其接口。它们的意图是不一样的,适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口。

八、模板方法模式 封装算法

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
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  abstract class Abstract Class {  
    //模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
    //申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序 
    final void cookProcess(){  
        //第一步:倒油
        this.pourOil()
        //第二步:热油
         this.HeatOil();
        //第三步:倒蔬菜
         this.pourVegetable();
        //第四步:倒调味料
         this.pourSauce();
        //第五步:翻炒
         this.fry();
    }  

    //定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的
    //第一步:倒油是一样的,所以直接实现
    void pourOil(){  
        System.out.println("倒油");  
    }  

    //第二步:热油是一样的,所以直接实现
    void  HeatOil(){  
        System.out.println("热油");  
    }  

    //第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
    //所以声明为抽象方法,具体由子类实现 
    abstract void  pourVegetable()

    //第四步:倒调味料是不一样的(一个下辣椒,一个是下蒜蓉)
    //所以声明为抽象方法,具体由子类实现 
    abstract void  pourSauce();
    
    //第五步:翻炒是一样的,所以直接实现
    void fry();{  
        System.out.println("炒啊炒啊炒到熟啊");  
    }  
}

// 具体模板
//炒手撕包菜的类
public class ConcreteClass_BaoCai extend  Abstract Class{
    @Override
    public void  pourVegetable(){  
        System.out.println(下锅的蔬菜是包菜);  
    }  
    @Override
    public void  pourSauce(){  
        System.out.println(下锅的酱料是辣椒);  
    }  
}
//炒蒜蓉菜心的类
public class ConcreteClass_CaiXin extend  Abstract Class{
    @Override
    public void  pourVegetable(){  
        System.out.println(下锅的蔬菜是菜心);  
    }  
    @Override
    public void  pourSauce(){  
        System.out.println(下锅的酱料是蒜蓉);  
    }  
}

//  做菜
public class Template Method{
  public static void main(String[] args){

    //炒 - 手撕包菜
    ConcreteClass_BaoCai BaoCai = new ConcreteClass_BaoCai();
    BaoCai.cookProcess()

    //炒 - 蒜蓉菜心
    ConcreteClass_ CaiXin = new ConcreteClass_CaiXin();
        CaiXin.cookProcess()
    }
}

//  做菜结果
倒油
热油
下锅的蔬菜是包菜
下锅的酱料是辣椒
炒啊炒啊炒到熟

倒油
热油
下锅的蔬菜是菜心
下锅的酱料是蒜蓉
炒啊炒啊炒到熟

2、封装算法

模板方法模式在一个方法中定义算法的框架,而将一些算法步骤延迟到子类中定义。使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。——即,封装算法

九、选代器与组合模式 管理良好的集合

1、组合模式定义

合成模式,有时又叫做部分-整体模式,主要是用来描述部分与整体的关系,将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。是安全模式和透明模式的实现方式

2、使用场景:

  • 只要是树形结构或者只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,就要考虑一下组合模式。
  • 从一个整体中能够独立出部分模块或功能的场景。
  • 维护和展示部分-整体关系的场景。

3、结构

image

  • Component抽象构件角色 定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。
  • Leaf叶子构件 Leaf叶子构件叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。
  • Composite树枝构件 树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。组合模式的重点就在树枝构件。

4、实现

(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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//  抽象构件
public abstract class Component {
    //个体和整体都具有
    public void operation(){
        //编写业务逻辑
    }
}

// 树枝构件
public class Composite extends Component {
    //构件容器
    private List<Component> componentArrayList = new ArrayList<Component>();
    //增加一个叶子构件或树枝构件
    public void add(Component component){
        this.componentArrayList.add(component);
    }
    //删除一个叶子构件或树枝构件
    public void remove(Component component){
        this.componentArrayList.remove(component);
    }
    //获得分支下的所有叶子构件和树枝构件
    public List<Component> getChildren(){
        return this.componentArrayList;
    }
}

// 树叶构件
public class Leaf extends Component {
    /*
    * 可以覆写父类方法
    * public void operation(){
    *
    * }
    */
}

//  Client
public class Client {
    public static void main(String[] args) {
        //创建一个根节点
        Composite root = new Composite();
        root.operation();
        //创建一个树枝构件
        Composite branch = new Composite();
        //创建一个叶子节点
        Leaf leaf = new Leaf();
        //建立整体
        root.add(branch);
        branch.add(leaf);
    }

    //通过递归遍历树
    public static void showTree(Composite root){
        for(Component c:root.getChildren()){
            if(c instanceof Leaf){ //叶子节点
                c.operation();
            }else{ //树枝节点
                showTree((Composite)c);
            }
        }
    }
}
(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
//  抽象构件
public abstract class Component {
    //个体和整体都具有
    public void operation(){
        //编写业务逻辑
    }
    //增加一个叶子构件或树枝构件
    public abstract void add(Component component);
    //删除一个叶子构件或树枝构件
    public abstract void remove(Component component);
    //获得分支下的所有叶子构件和树枝构件
    public abstract List<Component> getChildren();
}

//  树枝构件
public class Composite extends Component {
    //构件容器
    private ArrayList<Component> componentArrayList = new ArrayList<Component>();
    //增加一个叶子构件或树枝构件
    public void add(Component component){
        this.componentArrayList.add(component);
    }
    //删除一个叶子构件或树枝构件
    public void remove(Component component){
        this.componentArrayList.remove(component);
    }
    //获得分支下的所有叶子构件和树枝构件
    public List<Component> getChildren(){
        return this.componentArrayList;
    }
}

//  树叶构件
public class Leaf extends Component {

    public void add(Component component){
        //空实现
    }

    public void remove(Component component){
        //空实现
    }

    public List<Component> getChildren(){
        //空实现
    }
}

//  client
public class Client {

    public static void main(String[] args) {
        //创建一个根节点
        Composite root = new Composite();
        root.operation();
        //创建一个树枝构件
        Composite branch = new Composite();
        //创建一个叶子节点
        Leaf leaf = new Leaf();
        //建立整体
        root.add(branch);
        branch.add(leaf);
    }

    //通过递归遍历树
    public static void showTree(Component root){
        for(Component c:root.getChildren()){
            if(c instanceof Leaf){ //叶子节点
                c.operation();
            }else{ //树枝节点
                showTree(c);
            }
        }
    }
}

参考

十、状态模式 事物的状态

1、定义

在状态模式(StatePattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

2、优点确定

优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

3、使用场景:

1、行为随状态改变而改变的场景。2、条件、分支语句的代替者。

十一、代理模式 控制对象访问

1、定义

一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。 在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

2、优缺点

优点: 1、职责清晰。 2、高扩展性。 3、智能化。 缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

3、使用场景:

按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。

注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

十二、复合模式 模式中的模型

1、定义描述

复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。

2、MVC模式 = 策略模式 + 观察者模式 + 组合模式

image

上面这幅图描述的就是MVC模式,下面根据这幅图对MVC进行一下解释。

(1)、你是用户—你和视图交互

视图是模型的窗口。当你对视图做一些事事(比方说:按下“播放”按钮),视图就告诉控制器你做了什么。控制器会负责处理。

(2)、控制器要求模型改变状态。

控制器解读你的动作。如果你按下某个按钮,控制器会理解这个动作的意义,并告知模型如何做出对应的动作。

(3).控制器也可能要求视图做改变。

当控制器从视图接收到某一动作,结构可能是它也需要告诉视图改变其结果。比方说,控制器可以将界面上的某些按钮或菜单项变成有效或无效。

(4).当模型发生改变时,模型会通知视图。

不管是你做了某些动作(比方说按下按钮)还是内部有了某些改变(比方说播放清单的下一首歌开始)只要当模型内的东西改变时,模型都会通知视图它的状态改变了。

(5).视图向模型询问状态。

视图直接从模型取得它显示的状态。比方说,当模型通知视图新歌开始播放,视图向模型询问歌名并显示出来。当控制器请求视图改变时,视图也可能向模型询问某些状态。

找出各种模式: (1).策略模式

1
视图和控制器实现了策略模式:视图是一个对象,可以被调整使用不同的策略,而控制提供了策略。视图只关心系统中可视的部分,对与任何界面行为,都委托给控制器处理。使用策略模式也可以让视图和模型之间关系解耦,因为控制器负责和模型交互来传递用户的请求。对与工作是怎么完成的,视图豪不知情。

(2).观察者模式

1
模型实现了观察者模式,当状态改变时,相关对象将持续更新。使用观察者模式,可以让模型完全独立于视图和控制器。同一个模型可以使用不同的视图,甚至可以同时使用多个视图。

(3).组合模式

1
显示包括了窗口、面板、按钮、文本标签等。每个显示组件如果不是组合节点(例如窗口),就是叶节点(例如按钮)。当控制器告诉视图更新时,只需告诉视图最顶层的组件即可,组合会处理其余的事。

十三、与设计模式相处 真实世界中的模式

1、模式:某个情景下,针对某个问题的解决方案

2、分类:创建型、行为型、结构型、

3、如何做:

(0)、用模式思考 (1)、保持简单 (2)、重构的时间就是模式的时间 (3)、拿掉你所不需要的,不要害怕讲一个模式从你的设计中删除 (4)、如果现在不需要,就别做 …

image

十四、附录A:剩下的模式

  • 1、桥接模式:改变实现+改变抽象 例子:遥控器+电视机
  • 2、生成器模式: 例子:斐波拉契数列的推算规则
  • 3、责任链模式: 例子:事件传递机制
  • 4、蝇量模式:
  • 5、解释器模式:
  • 6、中介者模式:是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。 例如:DeviceManager、聊天室、等
  • 7、访问者模式:在访问者模式中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
  • 8、备忘录模式: 例子:游戏中复活、进度恢复、
本文由作者按照 CC BY 4.0 进行授权

xCode-控制台命令

xCode-常用快捷键

Comments powered by Disqus.