23种设计模式学习和总结
##简单工厂
理解:抽象功能至顶层接口,各子功能通过实现顶层接口实现各自功能。创建静态工厂类,通过不同类型创建不同的实现类型。用到继承,封装,多态。 概述:简单工厂模式又叫静态工厂方法模式,它定义了一个具体的工厂类负责创建一些类的实例。
#优点:
- 客户端不需要再负责对象的创建,从而明确了各个类的职责。
- 客户端只需要使用类,工厂来负责创建具体实现类即可。
简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类。对于客户端来说,去除了与具体产品的依赖。
#缺点: 静态类负责所有对象的创建,如果有新的对象增加或者某些对象的创建方式不同,需要频繁需改工厂类,不利于后期维护。
作用:简单工厂模式只能解决对象创建问题,工厂本身需要包含所有创建类型的条件。
##策略模式
定义:它定义了算法家族,分别封装起来,让他们之间乐意互相替换,此模式让算法的变化,不会影响到适应算法的客户。
#策略模式的思考 策略模式是定义一系列的算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少各种算法类与孙发之间的耦合。
策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。
策略模式优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试。
总结:策略模式封装变化
#实用场景 策略模式就是用来封装算法,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同业务规则,就可以考虑实用策略模式处理这种变化的可能性。
##装饰模式
定义:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。
核心实现类
扩展类 --》 使用多个扩展方法和核心进行组合
装饰模式是为已有功能动态地添加更多功能的一种方式
##代理模式
定义:为其他对象提供一种代理以控制这个对象访问
理解:隐藏真实对象的实现,代理对象就是真实对象的代表。使用代理模式可以在访问真实对象实加一些权限校验等。
#代理模式分类
远程代理:也就是一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
虚拟代理:是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
安全代理:用来控制真实对象的访问权限。
智能索引:是指当调用真实的对象时,代理处理另外一些事。
##工厂方法
定义:一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现具体实现类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。 想要加功能,本来是改工厂类的,而现在是修改客户端。 简单工厂违背了 开放 -封闭 原则
#简单工厂就解决了违背开放封闭原则,又保持了封装对象创建过程的优点
##原型模式
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。
浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
深拷贝:深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
#特点:
- clone对象不会执行对象的构造方法
- 由原型对象自身创建目标对象,也就是说,对象创建这一动作发自原型对象本身。
- 目标对象时原型对象的一个克隆,也就是说,通过原型模式创建的对象,不仅仅与原型对象具有相同的结构,还与原型对象具有相同的值。
- 根据对象克隆深度层次的不同,有浅度克隆与深度克隆。
#使用场景:
在初始化的信息不发生变化的情况下,克隆是最好的办法,这既隐藏了对象的创建细节,又对性能是大大的提高。
- 在创建对象的时候,我们不只是希望被创建的对象继承其积累的基本结构,还希望继承原型对象的数据。
- 希望对目标对象的修改不影响既有的原型对象(深度克隆的时候可以完全互不影响)。
- 隐藏克隆操作的细节,很多时候,对对象本身的克隆需要涉及到类本身的数据细节。
- 类初始化需要消化非常多的资源,这个资源包括数据,硬件资源等。
- 通过new产生一个对象需要非常繁琐的数据数据准备货访问权限,则可以使用原型模式。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值,可以考虑使用原型模式拷贝多个对象供调用者使用。 在实际项目中,原型模式很少单独出现,一般是和工厂模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给 调用者。原型模式先产生出一个包含大量共有信息的类,然后通过clone拷贝出副本,修正细节信息,建立一个完整的个性对象
##模板方法模式
定义:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
#特点
- 模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。
- 模板方法模式就是提供一个很好的代码复用平台。
#使用场景
- 当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。可以通过模板方法模式把这些 行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。
#优缺点
模板方法多个子类有公共的方法,并且逻辑基本相同时,重复的算法,可以把核心算法设计为模板方法,周边的相关细节功能由各个子类实现。
优点:
- 封装不变部分,扩展可变部分。
- 提取公共部分代码,便于维护。
- 行为有父类控制,子类实现。
缺点:
- 容易带来代码阅读的难度。
##软件设计原则
-
单一职责:就一个类而言,应该仅有一个引起变化的原因。 #思考 1.1 如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。 1.2 软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。 1.3 如果你能够想到多于一个的冬季去改变一个类,那么这个类就有多于一个的职责。
-
开放封闭原则:是说软件实体(类,模块,函数)应该可以扩展,但是不可以修改 #思考 2.1 面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码 2.2 开放封闭原则是面向对象设计的核心所在。遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护,可扩展,可服用,灵活性好。开发人员应该仅对程序中呈现出频繁变化的那些部分 做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。
-
依赖倒置原则
抽象不应该依赖细节,细节应该依赖抽象。就是面向接口编程,不要对实现编程。
3.1 高层模块不应该依赖低层模块,两个都应该依赖抽象。 3.2 抽象不应该依赖细节。细节应该依赖抽象。
-
里氏转换原则:子类型必须能够替换掉他们的父类型。
-
迪米特法则:如果两个类不彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另外一个类的某一个方法的话,可以通过第三者转发这个调用。
5.1 在类的结构设计上,每一个类都应当尽量降低成员的访问权限。 5.2 迪米特法则基本**是强调类之间的松耦合。 5.3 类之间耦合越弱,越有利于复用,一个处于弱耦合的类被修改,不会对有关系的类造成波及。
##外观模式 定义:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
#使用场景
- 在设计初期阶段,应该要有意识的将不同的两个层分离,在层与层之间建立外观Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合大大降低。
- 维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为他包含非常重要的功能,新需求开发必须依赖于它,可以提供外观Facade将功能整合成简单接口提供外部系统调用。
##建造者模式 定义:将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式又叫生成器模式,它可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程具有不同的内部表象的产品对象。用户只需要指定需要建造的类型就可以得到他们,而具体建造的过程和细节就不需要知道了。
Builder:是为创建一个Product对象的各个部件指定的抽象接口。 ConcreteBuilder: 他是具体的建造者,实现Builder接口,构造和装配各个部件。 Product: 具体创建的产品 Director: 他是构建一个使用Builder接口的对象
#使用场景
- 他主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部构建通常面临着复杂的变化。
建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
##观察者模式 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。
观察者模式所做的工作其实就是在解除耦合。让耦合的双方都在依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
##抽象工厂模式 定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
优点:
- 易于交换产品系列,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,他只需要改变具体工厂即可使用不同的产品配置。
- 它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。