23种设计模式详解

设计模式是代码开发人员总结归纳出的代码设计经验,使用合理的设计模式能够提高代码的可拓展性和可维护性。

本文主要介绍了设计模式开山鼻祖经典书籍《Design Patterns》描写的23种设计模式,不过正如GoF所说“We don’t consider this collection of design patterns complete and static; it’s more a recording of our current thoughts on design.(我们并不认为这些设计模式是完整并且一成不变的,他更多的只是对我们当前设计思想的记录。)”设计模式远不止23种,并且也并非是一种固定的定式。此书出版于1994年,在长久的发展中自然出现了许多变化和进步,其中有些经典模式也已不再常用,但从其中23种设计模式仍能学到某些思想。

总而言之,创造力是自己的,设计模式是通过经验总结给予的一种方向的指引。

1. 设计模式分类:

GoF将设计模式分为三类,分别为创造型设计模式、结构型设计模式、行为型设计模式,本文将对其一一列举。

在这里插入图片描述

2. 创建型模式(Creational)

创建型设计模式关注点在于如何创建对象,创建型模式用于解耦对象的实例化过程,主要特点是将对象的创建与使用分离。

1. 工厂方法(Factory Method)

工厂方法将创建对象和使用对象相分离,并且在使用时引用的是工厂和产品的抽象产品。

在这里插入图片描述

//工厂接口
public interface Factory {
    //接口中定义的变量默认为public static final
    Factory factory = new FactoryImpl();
    //java8后接口里可以声明静态方法,并可以实现,修饰符默认为public
    static Factory getFactory() {
        return factory;
    }
    //获取产品
    Product getProduct();
}
//工厂接口的实现
public class FactoryImpl implements Factory {
    @Override
    public Product getProduct() {
        return new ProductImpl();
    }
}
//产品接口
public interface Product {
    void say();
}
//产品接口的实现
public class ProductImpl implements Product{
    public void say() {
        System.out.println("hello factory method")
    }
}
//具体使用
public class MainTest {
    public static void main(String[] args) {
        Factory factory = Factory.getFactory();		//获取工厂类
        Product product = factory.getProduct();		//通过工厂获取产品
        product.say();		//输出结果:hello factory method
    }
}
2. 抽象工厂(Abstract factory)

抽象工厂和工厂方法稍微有些不同,抽象工厂中有多个产品需要创建,因此会有多个实际工厂,每个工厂又能创建多个实际产品。

就好比一台手机需要cpu、内存、屏幕等许多组件,其中的组件可能是由a厂生产,也可能是由b厂生产。

在这里插入图片描述

//假定有两个工厂A和B,先定义抽象工厂接口和产品接口
public interface AbstractFactory {
    FirstProduct getFirstProduct();		//获取第一个产品
    SecondProduct getSecondProduct();	//获取第二个产品
}
public interface FirstProduct {
    void sayHi();
}
public interface SecondProduct {
    void sayHello;
}
//定义第一个工厂和第一个工厂生成的产品
public class FactoryA implements AbstractFactory {
    public FirstProduct getFirstProduct() {
        return new FirstProductA();
    }
    public SecondProduct getSecondProduct() {
        return new SecondProductA();
    }
}
public class FirstProductA implements FirstProduct {
    public void sayHi() {
        System.out.println("hi,A product");
    }
}
public class SecondProductA implements SecondProduct {
    public void sayHello {
        System.out.println("hello,A product")
    }
}
//定义第二个工厂与第一个工厂工厂代码类似,只需把A换为B,方便起见简写
public class FactoryB implements AbstractFactory {...}
public class FirstProductB implements FirstProduct {...}
public class SecondProductB implements SecondProduc {...}
//具体使用
public class MainTest {
    public static void main(String[] args) {
        Factory factory = new FactoryA();		//使用A工厂
        FirstProduct firstProduct = factory.getFirstProduct();	//通过A工厂获取产品1
        SecondProduct secondProduct = factory.getSecondProduct();	//通过A工厂获取产品2
        firstProduct.sayHi();		//输出结果:hi,A product
        secondProduct.sayHello();	//输出结果:hello,A product
        //若用B工厂只需把 Factory factory = new FactoryA() 改为 new FactoryB(),下文省略
        ...
        ...
    }
}

此外如果在AbstractFactory中创建工厂,则具体的工厂都封装屏蔽了。

public interface AbstractFactory {
    //调用方法时通过传参进行工厂选择
    static AbstractFactory getFactory(String factoryName) {
        switch(factoryName) {
            case "A":
                return new FactoryA();
            case "B":
                return new FactoryB();
            default:
                throw new IllegalArgumentException();
        }
    }
    FirstProduct getFirstProduct();		//获取第一个产品
    SecondProduct getSecondProduct();	//获取第二个产品
}
//以下省略……
3. 建造者(Builder)

建造者模式也称为创建者模式,也有人叫生成器、构造器,主要是创建对象时对象参数比较多,通过每一个组件的拼接最终组合成一个完整的对象。

以下将通过lombok的注解来演示。(使用lombok可以简化很多代码,关于lombok的内容请自行搜索)

建造者模式的应用:

//声明一个“人”类,以下注解皆为lombok中的注解。
@Builder	//此注解为你的类生成相对略微复杂的构建器API
@Getter		//代替get方法
@Setter		//代替setter方法
@ToString	//重写toString方法
public class Person {
    private String name;
    private int age;
    private String gender;
    private String phoneNumber;
}
//使用
public class MainTest {
    public static void main(String[] args) {
        Person p = Person.builder().name("Tom").age("123").gender("male").build();
        //输出结果:Person(name=Tom, age=123, gender=male, phoneNumber=null)
        System.out.println(p);
        p.setPhoneNumber("123456");
        输出结果:Person(name=Tom, age=123, gender=male, phoneNumber=123456)
        System.out.println(p);
    }
}
4. 原型(Prototype)

原型模式是通过现有的一个原型来进行创建,主要用来创建重复的对象。如果当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

例如Arrays.copyof()方法实际就是一种原型模式。

//原型
String[] original = {"qqq","www","eee"}
String[] copy = Arrays.copyof(original,original.length);

对于普通类而言,如要实现原型拷贝需要实现Cloneable接口,然后通过Object类的clone()方法进行克隆。

5. 单例(Singleton)

单例模式保证了一个类仅有一个实例,并提供一个访问它的全局访问点。

通过如下方式可以实现一个简单的单例模式。

public class SingleObject {
    //创建SingleObject的一个对象
    private static SingleObject instance = new SingleObject();
    //使用private修饰构造函数,避免该类被实例化
    private SingleObject(){}
    //提够一个获取唯一对象的方法
    public static SingleObject getInstance() {
        return instance
    }
}

以上这种方式成为饿汉式,在类加载的时候便会实例化,也因此他是线程安全的。

除此之外还有一种懒汉式加载方式:

public class SingleObject {
    private static SingleObject instance;
    //private构造方法保证外部无法实例化
    private SingleObject();
    
    public static SingleObject getInstance() {
        //访问该方法时,若instance未实例化则进行实例化
        if(instance == null) {
            instance = new SingleObject();
        }
        return instance;
    }
}

懒汉式在多线程中是错误的,因为在竞争条件下会创造出多个实例,此时需要对整个方法进行加锁。

//对方法进行加锁,被锁的对象是当前类对象。该方式会严重影响并发性能。
public static synchronized SingleObject getInstance() {
    //访问该方法时,若instance未实例化则进行实例化
    if(instance == null) {
        instance = new SingleObject();
    }
    return instance;
}

另一种实现Singleton的方式是利用Java的enum,因为Java保证枚举类的每个枚举都是单例,所以我们只需要编写一个只有一个枚举的类即可。

此外有一种双重校验的锁的方式:

public class Singleton {
   private static Singleton instance;
   private Singleton(){}
   public static Singleton getInstance() {
       //控制阻塞条件
       if(instance == null) {
           synchronized(Singleton.class){
               //控制实例创建对象
               if(instance == null) {
                   instance = new Singleton();
               }
           }
       }
       return instance;
   }
}

上述方式看似很完美,但由于JVM即时编译器中存在指令重排序的优化,导致可能会产生某些错误(即DCL失效问题)。

此时需要将instance变量声明称volatile,此时使用volatile并非为了使其可见,而是禁止指定重排序优化

此外可采取静态内部类的方式,外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化instance,故而不占内存。该方式不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

public class Singleton{
   private static class SingletonHolder {
       private static Singleton instance = new Singleton();
   }
   private Singleton() {}
   public static Singleton getInstance() {
       return SingletonHolder.instance;
   }
}

//TODO

3. 结构型模式(Structural)

1. 适配器(Adapter)
2. 装饰器(Decorator)
3. 代理(Proxy)
4. 桥接(Bridge)
5. 组合(Composite)
6. 外观(Facade)
7. 享元(Flyweight)

4. 行为型模式(Behavioral)

1. 责任链(Chain of responsibility)
2. 命令(Command)
3. 解释器(Interpreter)
4. 迭代器(Iterator)
5. 中介者(Mediator)
6. 备忘录
7. 观察者(Observer)
8. 状态(State)
9. 策略(Strategy)
10. 模板方法(Template method)
11. 访问者(Visitor)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值