目录
前十三章涵盖了大部分面向对象程序设计的规则,以及许多有用的常用的设计模式。
规则
-
单一职责原则
-
开发封闭原则
-
依赖倒转原则
-
里氏替换原则
-
迪米特法则
单一职责是指对于一个类而言,仅且只有一个能够引起它变化的因素。
开放封闭原则是指软件实体(类、模块、函数等)进行拓展是可以的,但是修改它们是不可以的。
依赖倒转原则是指软件要针对抽象的接口进行编程,不能针对于具体实现进行编程。细节应该依赖于抽象。
里氏替换原则是指在软件实体中,任何基类可以出现的地方,子类一定可以出现,并且程序的行为不会因此改变。
迪米特法则是指如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
1、创建型模式
简单工厂模式
是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
优点
工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。
缺点
由于工厂类集中了所有实例的创建逻辑,违反了开放-封闭原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。
适用范围
简单工厂模式:工厂类负责创建的对象比较少,客户只知道传入了工厂类的参数,对于始何创建对象(逻辑)不关心。
https://lh-kevin.iteye.com/blog/1981574
工厂模式
又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭 原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。
优点:
- 相较于简单工厂模式而言,解决了开放封闭原则。
- 子类提供挂钩。基类为工厂方法提供缺省实现,子类可以重写新的实现,也可以继承父类的实现。加一层间接性,增加了灵活性
- 屏蔽产品类。调用者只需关心产品的父类,只要接口保持不变,系统中的上层模块就不会发生变化。
- 典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类都不需要关心,符合迪米特法则,符合依赖倒置原则,符合里氏替换原则。
- 多态性:客户代码可以做到与特定应用无关,适用于任何实体类。
- 系统增加新的产品时只需要添加具体产品类和一个具体工厂类,无需修改原工厂类。
缺点:
- 需要Creator和相应的子类作为factory method的载体,如果应用模型确实需要creator和子类存在,则很好;否则的话,需要增加一个类层次。
- 每增加一个产品就需要增加一个对应的产品工厂,增加了开发量。
- 假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦。
适用范围
工厂方法模式:当一个类不知道它所必须创建对象的类或一个类希望由子类来指定它所创建的对象时,当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候,可以使用工厂方法。
https://lh-kevin.iteye.com/blog/1981574
建造者模式
将一个复杂对象的构造与它表示进行分离,使同样的构造过程可以创建不同的表示意图。
用户只需要指定需要建造的类型就可以得到它们,而具体的建造过程和细节就不需要知道了。
将复杂的对象分解成多个简单的对象,然后一步一步构建起来。将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
优点
- 将产品与产品本身创建过程进行解耦,可以使用相同的构建过程来得到不同的产品。
- 各个具体的建造者相互独立,有利于系统的拓展。
- 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,便于控制细节风险。
- 将建造代码与表示代码分离,若要改变产品的内部表示,只需要再定义一个具体的建造者就可以了。
缺点
- 产品的组成部分必须相同,限制了其使用。
- 如果产品的内部变化复杂,该模式会增加很多的建造者类。
应用场景
- 需要生成的对象拥有复杂的内部结构,由多个构建构成,但构件间的构造过程是稳定的。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
- 创建复杂对象的算法独立于该对象的组成部分以及他们的装配部分,即产品的构建过程和最终的产品表示是独立的。
http://c.biancheng.net/view/1354.html
2、结构型模式
装饰模式
是在不必改变原类文件和使用继承的情况下,动态地给一个对象扩展一些功能,属于对象结构性模式。
就增加对象功能来说,装饰模式比继承生成子类实现更加灵活,通常情况下,拓展一个类的功能会使用继承方法来实现。但继承具有静态特征,耦合度高,并且随着拓展功能的增多,子类会很膨胀。如何使用组合关系来创建一个包装对象(装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是该模式的目标。
优点
- 采用装饰模式拓展对象比采用继承方式更加灵活
- 可以通过一种动态的方式拓展一个类的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
- 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创建出很多不同的行为组合,得到功能更加强大的对象
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无需改变,复合开放-封闭原则。
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
缺点
- 使用这个模式进行系统设计时会产生很多小对象,可能会占用更多的系统资源。
- 装饰模式提供了比继承更加灵活的方案,但意味着比继承更加容易出错,排错更加困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为频繁。
适用场景
- 当对象的功能可以动态地添加,而且可以动态地撤销时。
- 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却能很好实现。
- 当不能采用继承的方式对系统进行拓展或者采用继承不利于系统拓展和维护时可以使用装饰模式。
不能采用继承的情况有两类:
第一类是系统存在大量的独立拓展,为支持每一种拓展或者拓展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;
第二类是拓展功能时,类已定义为不能为继承时(比如java 的final类)。
https://blog.csdn.net/wwwdc1012/article/details/82764333
外观模式
提供一个统一的高层接口去访问多个不同的接口,让子系统更容易被访问的模式。
该模式有一个对外的统一接口,外部应用程序不用关心内部子系统的具体细节,大大降低了应用程序的复杂性,提高了程序的可维护性。是迪米特法则的典型应用。
一个系统可以有多个外观类;不要试图通过外观类为子系统增加新行为;外观类与迪米特法则,抽象外观类的引入可以解决违背开闭原则这个缺点。https://blog.csdn.net/xingjiarong/article/details/50066133
优点
- 降低了子系统与客户端的耦合度,使得子系统的变化不会影响到调用它的客户端类,减少了系统之间的相互依赖。
- 对客户屏蔽子系统的组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
- 通过合理使用facade,更好地划分了访问地层次,有些方法是对系统外地,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部细节,提高安全性。
缺点
- 没有面向抽象编程,而是增加中介层,转换服务提供方的服务接口。
- 增加新的子系统可能需要修改外观类或客户端类,违背了“开放-封闭原则”。
适用场景
- 简化子系统复杂性时;当要为一个复杂子系统提供一个简单接口时可以使用该模式。该接口可以满足大多数用户的需求,用户也可直接访问子系统。
- 希望封装和隐藏子系统时;客户端与多个子系统之间存在很大的依赖性,引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可以执行。
- 监控所有子系统时;通过门面控制了入口,可以统一监控;在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。
代理模式
是指给一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
某些情况下,一个对象不适合或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
其特征是代理类和委托类实现相同的接口。
代理模式还分普通代理、强制代理、自定义代理、动态代理。https://blog.csdn.net/king123456man/article/details/82767576
java中的代理有静态代理,动态代理,CGLib代理。https://cloud.tencent.com/developer/article/1356052
优点
- 在客户端与目标对象之间起到一个中介和保护目标对象的作用。
- 代理模式能将代理对象与真实被调用的目标对象分离,一定程度上降低了系统的耦合度,耦合度好。
- 代理模式能拓展目标对象的功能。
- 却
缺点
- 在客户端和目标对象之间增加一个代理对象,会使请求处理速度变慢。
- 实现代理类也需要额外的工作,从而增加了系统的复杂度。
应用场景
按职责来分,通常有如下使用场景:
- 远程代理
- 虚拟代理
- Copy-onWrite代理
- 保护代理
- 智能引用代理
- 同步化代理
- Cache代理
- 防火墙代理
当需要为一个对象再不同的地址空间提供局部的代表时
此时的代理模式称为远程代理:为一个位于不同的地址空间的对象提供一个本地的代理对象。
目的:
隐藏一个对象存在于不同地址空间的事实;
远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
当需要创建开销非常大的对象时
此时的代理模式称为虚拟代理:创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
目的:减少系统的开销。
Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
当需要控制对原始对象的访问时
此时的代理模式称为保护代理:控制目标对象的访问,给不同用户提供不同的访问权限
目的:用来控制对真实对象的访问权限
当需要在访问对象时附加额外操作时
此时的代理模式称为智能引用代理,取代了简单的指针,它在访问对象时执行一些额外操作。额外操作包括耗时操作、计算访问次数等等
目的:在不影响对象类的情况下,在访问对象时进行更多的操作
以上是最常用的使用场景,其他还包括:
同步化代理:使几个用户能同时使用一个对象而没有冲突。
防火墙代理:保护目标不让恶意用户靠近。
Cache代理:为结果提供临时的存储空间,以便其他客户端调用
https://blog.csdn.net/carson_ho/article/details/54910472
https://blog.csdn.net/hguisu/article/details/7542143
行为型模式
模板方法模式
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
提高了代码的复用性,将相同代码放入父类中,将不同的代码放入子类中;实现了反向控制,通过一个父类调用其子类的操作,通过对子类的具体实现,拓展不同的行为,遵守了依赖反转和开放封闭原则。为了防止恶意操作,一般模板类都加上final关键字。
优点
- 封装不可变的部分,拓展可变的部分。把不可变的算法封装到父类中,
- 提取公共代码,便于维护。
- 行为由父类控制,子类实现,实现了控制反转。通过一个父类调用子类实现的操作,通过子类扩展增加新的行为。符合开放封闭原则和控制反转原则。
缺点
- 每一个不同的实现都需要一个子类来实现,具体实现过多的话会导致类的数目增大,使得系统非常庞大。
- 父类中的抽象方法由子类实现,子类的执行结果会影响父类的结果,这一种反向的控制结构,使阅读代码的难度提高。
应用场景
- 当子类存在公共的行为,且逻辑相同,可以提取出来并集中到一个父类中,避免代码重复。
- 算法的整体步骤很固定,但其中个别易变时,可以用模板方法将易变的部分抽象出来,供子类实现。
- 当需要控制子类的拓展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行拓展。
http://c.biancheng.net/view/1376.html
策略模式
是指定义一系列的算法,将每个算法封装到具有公共接口的一系列策略类中,使它们可以相互替换,且算法的变化不会影响到使用算法的客户端。
通过将算法的责任和算法的实现分开来,并委派给不同的对象对这些算法进行管理。
使用策略模式可以直接传入策略类的子类指定策略要生成的实例。
优点
- 对算法的选择中,多重条件语句不易维护,而策略模式可以避免这个问题
- 提供一系列可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类,避免重复。
- 可提供相同行为的不同实现,客户可根据不同时间或空间要求选择不同的实现。
- 对开放封闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新代码。
- 把算法的选择放到context类中,而算法的具体实现转移到具体策略类中,实现了二者分离。
缺点
- 客户端必须理解所有策略算法的区别,才能选择到恰当的算法,并且所有策略类都是对外暴露的。
- 策略模式会造成很多策略类。可以通过使用享元模式在一定程度上减少对象的数量。
适用场景
一个系统需要动态地选择用到多种算法,可以将每个算法封装到策略类中。
一个类中定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个分支移入它们各自的策略类中以代替那些语句。
系统中要求彼此完全独立,且要求对客户端隐藏算法的具体细节时。
系统要求使用算法的客户端不知道其操作的数据时,可使用策略模式隐藏与算法相关的数据结构。
多个类只区别在表现行为不同时,可以使用策略模式,在运行时动态选择具体要执行的行为。
http://c.biancheng.net/view/1378.html https://www.runoob.com/design-pattern/strategy-pattern.html