一、设计模式的分类
设计模式可以分为三大类:
(1) 创建型模式
- 抽象工厂模式:http://blog.csdn.net/oDeviloo/article/details/52471559
- 生成器模式:http://blog.csdn.net/oDeviloo/article/details/52684935
- 工厂方法模式:http://blog.csdn.net/oDeviloo/article/details/52464359
- 原型模式:http://blog.csdn.net/oDeviloo/article/details/52760783
- 单例模式:http://blog.csdn.net/oDeviloo/article/details/52388833
(2) 结构型模式
- 适配器模式:http://blog.csdn.net/oDeviloo/article/details/52535105
- 桥接模式:http://blog.csdn.net/oDeviloo/article/details/52816069
- 组合模式:http://blog.csdn.net/oDeviloo/article/details/52839170
- 装饰者模式:http://blog.csdn.net/oDeviloo/article/details/52488283
- 外观模式:http://blog.csdn.net/oDeviloo/article/details/52918773
- 享元模式:http://blog.csdn.net/oDeviloo/article/details/52458677
- 代理模式:http://blog.csdn.net/oDeviloo/article/details/52420690
(3) 行为模式
- 职责链模式:http://blog.csdn.net/oDeviloo/article/details/53022407
- 命令模式:http://blog.csdn.net/oDeviloo/article/details/53081455
- 解释器模式:http://blog.csdn.net/oDeviloo/article/details/53184547
- 迭代器模式:http://blog.csdn.net/odeviloo/article/details/53292643
- 中介者模式:http://blog.csdn.net/odeviloo/article/details/53292575
- 备忘录模式:http://blog.csdn.net/oDeviloo/article/details/53408610
- 观察者模式:http://blog.csdn.net/oDeviloo/article/details/52507234
- 状态模式:http://blog.csdn.net/oDeviloo/article/details/52600518
- 策略模式:http://blog.csdn.net/oDeviloo/article/details/52610961
- 模板方法模式:http://blog.csdn.net/oDeviloo/article/details/52671340
- 访问者模式:http://blog.csdn.net/oDeviloo/article/details/53462758
图片出处《设计模式》一书
二、设计模式六大原则
1. 单一职责原则
定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。如果一个类包含多种职责,就应该把类拆分。
场景:如果类A有两个职责:d1,d2。当职责d1需要修改时,可能会导致原本运行正常的职责d2功能产生问题。
方案:如果一个类包含多种职责,就应该把类拆分。分别建立两个类A、B,让A负责d1,B负责d2。当需要修改某一职责,那么将不会对另外一个功能产生影响。
2. 里氏替换原则
定义:这一原则与继承紧密相关。如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。所有引用基类的地方必须能透明地使用其子类的对象。
场景:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
如果我们需要增加一个新的功能:完成两数相加,然后再与100求和,由类B来负责。
那么类B则要完成两个功能:
- 两数相减
- 两数相加,然后再加100
由于类A已经实现了第一个功能,所以类B继承类A后,只需要再完成第二个功能就可以了,代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
方案:当使用继承时,遵循里氏替换原则。子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的形参要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的返回值要比父类更严格。
3. 依赖倒置原则
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
场景:类A的方法依赖类B,如果需要A通过C来实现同样的功能,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 1
- 2
- 3
假如有一天,我们不是给书而是给一份报纸,让这位母亲读下报纸上的事。报纸代码如下:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
我们会发现一个问题,我们需要修改原来的narrate,将入参修改。或者,我们可以新建一个方法。但是,这样也需要修改Mother的代码。使用面向接口编程,会很好的解决这个问题
- 1
- 2
- 3
- 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
- 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
4. 接口隔离原则
定义:实现类不应该依赖它不需要实现接口具体方法的接口。
场景:接口A需要实现方法a、b、c,具体实现类B实现方法a、b,实现类C实现a、c。所以B,C需要实现它们不需要的方法。
方案:将接口A继续拆分为独立的几个接口,类B和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
5. 迪米特法则(最少知道原则)
定义:一个对象应该对其他对象保持最少的了解。也就是说:一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
场景:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
- 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
- 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
现在这个设计的主要问题出在EmployeeService中,根据迪米特法则,只与直接的朋友发生通信,而ChildEmployee类和EmployeeService类并不应该有直接关系,从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,而和分公司员工的相关操作应该交给分公司来处理。
方案:软件编程的总的原则:低耦合,高内聚。尽量降低类与类之间的耦合。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
6. 开闭原则
定义:对扩展开放,对修改关闭
场景:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误
方案:当软件需要变化时,不应该通过修改已有的代码来实现变化,而是尽量通过扩展原有代码。这是为了使程序的扩展性好,易于维护和升级。