上次看了设计模式的6大设计原则,单一职责原则、开闭原则、迪米特法则、里氏替换原则、接口隔离原则、依赖倒置原则。这次开始学习具体的23种设计模式。
一、单例模式
Ensure a class has only instance,and provide a global point of access to it.
确保某一个类只有一个实例,而且自行实例化并向整个系统提供整个实例。
类图:
单例模式的应用
单例模式的优点
减少内存开支;
减少系统的性能开销;
避免对资源的多重占用;
设置全局的访问点,优化和共享资源访问。单例模式的缺点
一般没有接口,扩展困难;
对测试不利;
单例模式和单一职责原则冲突。(单例模式把“要单例”和业务逻辑融合在一个类中)。单例模式的使用场景
要求生成唯一序列号的环境;
整个项目中需要一个共享访问点或者共享数据;
创建一个对象需要消耗的资源过多,例如访问IO和数据库等资源;
需要定义大量的静态常量和静态方法(如工具类)的环境。
- 单例模式的注意事项
1、在高并发情况下的线程安全问题:
饿汉式单例:(线程安全的单例)
public class SomeClient {
private static final SomeClient someClient = new SomeClient();
//限制产生多个对象
private SomeClient() {
}
//通过该方法获取实例对象
public static SomeClient getSomeClient() {
return someClient;
}
//类中其他方法,尽量是static
public static void doSomething() {
}
}
懒汉式单例:(线程不安全的单例)
public class SomeClient {
private static final SomeClient someClient = null;
//限制产生多个对象
private SomeClient() {
}
//通过该方法获取实例对象
public static SomeClient getSomeClient() {
if(someClient == null) {
someClient = new SomeClient();
}
return someClient;
}
}
2、考虑对象的复制情况
Java中对象默认是不可以被复制的,若实现了Cloneable接口,并实现了clone方法,则可以复制。解决的方法就是不要实现Cloneable接口。
- 单例模式的扩展
一个类产生多个对象,直接new就可以,如果只需要一个,使用单例就可以,如果是一个类需要两三个对象呢?
二、工厂方式模式
Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类中。
在工厂方法模式中,抽象产品类Product负责定义产品的共性,实现对事物最抽象的定义,Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂类ConcreteCreator完成的。
代码示例
抽象产品类
public abstract class Product {
//产品类的共用方法
public void method1 (){
//业务逻辑处理
}
//抽象方法
public abstract void method2();
}
具体产品类
public class ConcreteProduct1 extends Product {
public void method2(){
//业务逻辑处理
}
}
public class ConcreteProduct2 extends Product {
public void method2() {
//业务逻辑处理
}
}
抽象工厂类
public abstract class Creator {
/**
* 创建一个产品对象,其输入参数类型可以自行设置
* 通常为String、Enum、Class等,当然也可以为空
*/
public abstract <T extends Product> T createProduct(Class<T> c);
}
具体工厂类
public class ConcreateCreator extends Creator {
@Override
public <T extends Product> T createProduct(Class<T> c) {
Product product = null;
try {
product = (Product)Class.forName(c.getName()).newInstance();
}catch (Exception e) {
//异常处理
}
return (T)product;
}
}
调用方法的场景类
public class Client {
public static void main(String[] args) {
Creator creator = new ConcreateCreator();
Product product = creator.createProduct(ConcreteProduct1.class);
/**
* 继续业务处理
*/
}
}
工厂方法模式的应用
- 优点
- 良好的封装性,代码结构清晰。
- 扩展性非常好。
- 屏蔽产品类。
- 工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,可以使用产品子类替换产品父类。
- 使用场景
- 考虑增加一个工厂类进行管理会增加代码的复杂性。
- 需要灵活的,可以扩展的框架时,可以考虑采用工厂方法模式。
- 工厂方法模式可以用在异构项目中。
- 可以使用在测试驱动开发的框架下。
工厂方法模式的扩展
缩小为简单工厂模式
比如一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法就可以了,这样的模式是工厂方法模式的弱化版,也就是简单工厂模式,也叫静态工厂模式。
简单工厂模式就是没有工厂类,但也出现了扩展比较困难的缺点。多个工厂类
替代单例模式
延迟初始化
三、抽象工厂模式
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
为创建一组相关或互相依赖的对象提供一个接口,而且无须指定它们的具体类。
抽象工厂模式是工厂方法模式的升级版本,在有多个业务种类、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构。
在上面的类图中,两厢车和三厢车称为两个不同的等级结构;而2.0排量车和2.4排量车则称为两个不同的产品族。再具体一点,2.0排量两厢车和2.4排量两厢车属于同一个等级结构,2.0排量三厢车和2.4排量三厢车属于另一个等级结构;而2.0排量两厢车和2.0排量三厢车属于同一个产品族,2.4排量两厢车和2.4排量三厢车属于另一个产品族。
明白了等级结构和产品族的概念,就理解工厂方法模式和抽象工厂模式的区别了,如果工厂的产品全部属于同一个等级结构,则属于工厂方法模式;如果工厂的产品来自多个等级结构,则属于抽象工厂模式。在本例中,如果一个工厂模式提供2.0排量两厢车和2.4排量两厢车,那么他属于工厂方法模式;如果一个工厂模式是提供2.4排量两厢车和2.4排量三厢车两个产品,那么这个工厂模式就是抽象工厂模式,因为他提供的产品是分属两个不同的等级结构。当然,如果一个工厂提供全部四种车型的产品,因为产品分属两个等级结构,他当然也属于抽象工厂模式了。
抽象工厂模式代码
interface IProduct1 {
public void show();
}
interface IProduct2 {
public void show();
}
class Product1 implements IProduct1 {
public void show() {
System.out.println("这是1型产品");
}
}
class Product2 implements IProduct2 {
public void show() {
System.out.println("这是2型产品");
}
}
interface IFactory {
public IProduct1 createProduct1();
public IProduct2 createProduct2();
}
class Factory implements IFactory{
public IProduct1 createProduct1() {
return new Product1();
}
public IProduct2 createProduct2() {
return new Product2();
}
}
public class Client {
public static void main(String[] args){
IFactory factory = new Factory();
factory.createProduct1().show();
factory.createProduct2().show();
}
抽象工厂模式的应用
优点
抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。缺点
产品族扩展非常困难适用场景
一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式。
说的更明白一点,就是一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。假如各个等级结构中的实现类之间不存在关联或约束,则使用多个独立的工厂来对产品进行创建,则更合适一点。抽象工厂模式的注意事项
抽象工厂模式的缺点中,提到抽象工厂模式的产品族扩展比较困难,一定是产品族而不是产品等级。产品等级非常容易扩展。也就是说横向扩展容易,纵向扩展困难。
本章书中讲解由于没有清晰的产品族和产品等级的例子说明,看的有点模糊,整理的时候参照着 https://blog.csdn.net/zhengzhb/article/details/7359385 才理解,感谢~
四、模板方法模式
Define the skeleton of algorithm in an opration, deferring some steps to subclasses.Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
AbstractClass叫做抽象模板,它的方法分为两类:
- 基本方法
基本方法由父类声明并制定规范,由子类去实现 - 模板方法
模板方法父类声明并实现,子类不能修改
ConcreteClass1 和 ConcreteClass2为具体模板,实现父类AbstractClass中定义的基本方法。
模板方法模式的应用
- 优点
- 封装不变部分,扩展可变部分
不变部分的算法封装到父类实现,可变则通过继承来继续扩展。 - 提取公共部分代码,便于维护
- 行为由父类控制,子类实现
缺点
子类影响父类,在复杂的项目中带来了代码阅读的难度。适用场景
- 多个子类有公有的方法,并且逻辑基本相同时
- 重要、复杂的算法,可以把核心算法设计为模板方法,周边相关细节交给子类实现
- 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为
模板方法模式的扩展
钩子方法:抽象类去声明和实现,但是子类可以扩展,而子类的实现就可以影响到父类的模板方法执行结果,这种方法就叫钩子方法(Hook Method)。
父类建立框架,子类在重写了父类部分的方法后,再调用父类继承的方法,产生不同的结果,修改了子类,影响了父类行为的结果,这就是模板方法模式的效果(有了钩子方法的模板方法模式才算完美😀)