学习使用设计模式的思路
- 含义
- 解决的问题
- 模式原理
- 实例讲解
- 优点
- 缺点
- 应用场景
- 总结
A.简单工厂 SimpleFactory Pattern
-
简单工厂模式又叫静态方法模式(因为工厂类定义了一个静态方法),在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而
实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。 -
将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。
即使用者可直接消费产品而不需要知道其生产的细节 -
组成 关系 作用 抽象产品(Product) 具体产品的父类 描述产品的公共接口
具体产品 (Concrete Product) 抽象产品的子类;工厂类创建的目标类 描述生产的具体chanp
工厂 被外界调用 根据传入不同参数从而创建不同具体产品类的实例 -
创建抽象产品类 & 定义具体产品的公共接口;
创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
创建工厂类,通过创建静态方法根据传入不同参数从而创建不同具体产品类的实例;
外界通过调用工厂类的静态方法,传入不同参数从而创建不同具体产品类的实例 -
优点
将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦;
把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。 -
缺点
工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。 -
应用场景
客户如果只知道传入工厂类的参数,对于如何创建对象的逻辑不关心时;
当工厂类负责创建的对象(具体产品)比较少时。
B.工厂方法模式 Factory Method
-
工厂方法模式,又称工厂模式、多态工厂模式和虚拟构造器模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。
-
主要作用:
将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。 -
解决的问题:
工厂一旦需要生产新产品就需要修改工厂类的方法逻辑,违背了“开放 - 关闭原则
即简单工厂模式的缺点
之所以可以解决简单工厂的问题,是因为工厂方法模式把具体产品的创建推迟到工厂类的子类(具体工厂)中,此时工厂类不再负责所有产品的创建,
而只是给出具体工厂必须实现的接口,这样工厂方法模式在添加新产品的时候就不修改工厂类逻辑而是添加新的工厂子类,符合开放封闭原则,克服了简单工厂模式中缺点 -
组成 关系 作用 抽象产品(Product) 具体产品的父类 描述具体产品的公共接口 具体产品(Concrete Product) 抽象产品的子类;工厂类创建的目标类 描述生产的具体产品 抽象工厂(Create) 具体工厂的父类 描述具体工厂的公共接口 具体工厂(Concrete Create) 抽象工厂的子类;被外界调用 描述具体工厂;实现FactoryMethod工厂方法创建产品的实例
-
使用步骤
步骤1: 创建抽象工厂类,定义具体工厂的公共接口;
步骤2: 创建抽象产品类 ,定义具体产品的公共接口;
步骤3: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
步骤4:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
步骤5:外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例 -
优点
更符合开-闭原则
新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可PS:简单工厂模式需要修改工厂类的判断逻辑符合单一职责原则
每个具体工厂类只负责创建对应的产品 PS:简单工厂中的工厂类存在复杂的switch逻辑判断不使用静态工厂方法,可以形成基于继承的等级结构。 PS:简单工厂模式的工厂类使用静态工厂方法
总结优点:工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。
-
缺点
添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;
一个具体工厂只能创建一种具体产品 -
应用场景
当一个类不知道它所需要的对象的类时
在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可;
当一个类希望通过其子类来指定创建对象时
在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
C.抽象工厂Abstract Factory
工厂方法一个严重问题: 一个具体工厂只能创建一类产品
而在实际过程中,一个工厂往往需要生产多类产品。为了解决上述的问题,我们又使用了一种新的设计模式:抽象工厂模式。
1.定义
抽象工厂模式,即Abstract Factory Pattern,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。
PS :抽象工厂模式与工厂方法模式最大的区别:抽象工厂中每个工厂可以创建多种类的产品;而工厂方法每个工厂只能创建一类。
2.主要作用
允许使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么,这样就可以从具体产品中被解耦。
3.解决问题
每个类只能创建一类产品,即工厂方法的缺点
4.模式组成
组成 关系 作用
抽象产品族(AbstractProduct) 抽象产品的父类 描述抽象产品的公共接口
抽象产品(Product) 具体产品的父类 描述具体产品的公共接口
具体产品(Concrete Product) 抽象产品的子类;工厂类创建的目标类 描述生产的具体产品
抽象工厂(Creator) 具体工厂的父类 描述具体工厂的公共接口
具体工厂(Concrete Creator) 抽象工厂的子类;被外界调用 描述具体类;实现FactoryMethod工厂方法创建产品的实例
5.使用步骤
步骤1: 创建抽象工厂类,定义具体工厂的公共接口;
步骤2: 创建抽象产品族类 ,定义抽象产品的公共接口;
步骤3: 创建抽象产品类 (继承抽象产品族类),定义具体产品的公共接口;
步骤4: 创建具体产品类(继承抽象产品类) & 定义生产的具体产品;
步骤5:创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法;
步骤6:客户端通过实例化具体的工厂类,并调用其创建不同目标产品的方法创建不同具体产品类的实例
6.优点
降低耦合
抽象工厂模式将具体产品的创建延迟到具体工厂的子类中,这样将对象的创建封装起来,可以减少客户端与具体产品类之间的依赖,从而使系统耦合度低,这样更有利于后期的维护和扩展;
更符合 开-闭原则
新增一种产品类时,只需要增加相应的具体产品类和相应的工厂子类即可 PS :简单工厂模式需要修改工厂类的判断逻辑
符合单一职责原则
每个具体工厂类只负责创建对应的产品 PS:简单工厂中的工厂类存在复杂的switch的逻辑判断
不使用静态工厂方法,可以形成基于继承的等级结构。
PS :简单工厂模式的工厂类使用静态工厂方法
7.缺点
抽象工厂模式很难支持新种类产品的变化。
这是因为抽象工厂接口中已经确定了可以被创建的产品集合,如果需要添加新产品,此时就必须去修改抽象工厂的接口,
这样就涉及到抽象工厂类的以及所有子类的改变,这样也就违背了“开发——封闭”原则。
8.应用场景
一个系统不要求依赖产品类实例如何被创建、组合和表达的表达,这点也是所有工厂模式应用的前提。
这个系统有多个系列产品,而系统中只消费其中某一系列产品
系统要求提供一个产品类的库,所有产品以同样的接口出现,客户端不需要依赖具体实现。
D.单例模式 Singleton Pattern
实现1个类只有1个实例化对象 & 提供一个全局访问点。
保证1个类只有1个对象,降低对象之间的耦合度。
1.定义
一个类有且仅有一个实例,并且自行实例化向整个系统提供
2.优点
提供了对唯一实例的受控访问;
由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能;
可以根据实际情况需要,在单例模式的基础上扩展做出双例模式,多例模式;
3.缺点
单例类的职责过重,里面的代码可能会过于复杂,在一定程度上违背了“单一职责原则”。
如果实例化的对象长时间不被利用,会被系统认为是垃圾而被回收,这将导致对象状态的丢失。
4.单例模式的实现方式
根据需求场景 分为两大类 6种实现方式
A.初始化单例类时即创建单例
1.饿汉式
2.枚举类型
B.按需求,延迟创建单例
B1.懒汉式
1.基础实现
改进
2.同步锁
3.双重检验锁
静态内部类 实现
E.建造者模式 Builder Pattern
-
定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 -
主要作用
在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
用户只需要给出指定复杂对象的类型和内容;
建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来) -
解决的问题
方便用户创建复杂的对象(不需要知道实现过程)
代码复用性 & 封装性(将对象构建过程和细节进行封装 & 复用)
例子:造汽车 & 买汽车。
工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了
(不需要知道汽车是怎么组装的(车轮、车门、发动机、方向盘等等))
4.模式讲解:
指挥者(Director)直接和客户(Client)进行需求沟通;
沟通后指挥者将客户创建产品的需求划分为各个部件的建造请求(Builder);
将各个部件的建造请求委派到具体的建造者(ConcreteBuilder);
各个具体建造者负责进行产品部件的构建;
最终构建成具体产品(Product)。
5.实例讲解
a. 实例概况
背景:小成希望去电脑城买一台组装的台式主机
过程:
电脑城老板(Diretor)和小成(Client)进行需求沟通(买来打游戏?学习?看片?)
了解需求后,电脑城老板将小成需要的主机划分为各个部件(Builder)的建造请求(CPU、主板blabla)
指挥装机人员(ConcreteBuilder)去构建组件;
将组件组装起来成小成需要的电脑(Product)
6.优点
易于解耦
将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就是说细节依赖抽象。
易于精确控制对象的创建
将复杂产品的创建步骤分解在不同的方法中,是的创建过程更加清晰。
易于扩展
增加新的具体建造者无序修改原有类库的代码,易于拓展,符合"开闭原则"。总:每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
7.缺点
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
F.原型模式PrototypePattern
1.定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
2.使用场景
(1)类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。
(2)通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。
(3)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。
3.优点
(1)原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量对象时,原型模式可能更好的体现其优点。
(2)还有一个重要的用途就是保护性拷贝,也就是对某个对象对外可能是只读的,为了防止外部对这个只读对象的修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。
4.缺点
(1)这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题。
优点是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。
(2)通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,
只有当通过new构造对象较为耗时或者说成本较高时,通过clone方法才能够获得效率上的提升。
5.注意事项
1. 构造函数不会被执行
2. 深复制和浅复制
3. clone和final不同同时使用