博客说明:内容初稿为本人的学习笔记归纳整理,在此基础上加入了相关视频学习、相关书籍的理解、相关文章博客查阅及源码阅读。
博客的编写已经尽量做到详尽,但免不了有纰漏和理解不到位的地方。
发现博客的任何问题均可联系我 aboutwxf@163.com。谢谢!
目录
模式类型 | 特征 | 种类 |
---|---|---|
创建型模式 | 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式 而不是使用 new 运算符直接实例化对象。 这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 |
工厂模式(Factory Pattern) 工厂方法模式(Factory Method Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
结构型模式 | 这些设计模式关注类和对象的组合。 继承的概念被用来组合接口和定义组合对象获得新功能的方式。 | 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
行为型模式 | 这些设计模式特别关注对象之间的通信。 | 责任链模式(Chain of Responsibility) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern) |
1. 单例(Singleton)
解决的问题:在程序中多次使用同一个对象且作用相同时,频繁地创建对象使得内存飙升和额外的GC开销。
解决的方案:对该类创建一个唯一的实例,每次使用无则生成,有则使用
何时使用:想控制实例数目,节省系统资源的时候
实现-案例:
Ⅰ 懒汉式-线程不安全
懒汉式:只有在访问的时候才实例化这个对象。懒汉的好处是:如果没有用到该类,那么就不会实例化 ,就能节约资源。但是这个实现在多线程环境下是不安全的,如果多个线程能够同时进入代码1的判断,并且此时 uniqueInstance 为 null,那么会有多个线程执行 代码2语句,这将导致实例化多次 uniqueInstance。
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) { //---1
uniqueInstance = new Singleton(); //---2
}
return uniqueInstance;
}
}
Ⅱ 饿汉式-线程安全
饿汉式会在一开始就声明并初始化这个实例,这样方法不用判断就不会产生线程安全问题,但是没有懒汉式的节约资源的好处了
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {
}
public static Singleton getUniqueInstance() {
return uniqueInstance;
}
}
Ⅲ 懒汉式-线程安全
要懒汉式成为线程安全的就只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。但是就算已经实例化了也会造成其他线程拥堵在这个方法(已经实例化就没必要用这个锁了)。性能损耗大
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
Ⅳ 双重校验锁-线程安全
如果又要线程安全,又要使用时才实例化来节约资源,又要性能好。可以通过双重校验锁的方式实现,注意2和4两个判断都是必须的。如果没有4这个判断,多个线程会依次获取到锁,依次生成实例,那还是线程不安全的。注意1处加volatile保证线程可见性和避免指令重排。
public class Singleton {
private volatile static Singleton uniqueInstance; //---1
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) { //---2
synchronized (Singleton.class) { //---3
if (uniqueInstance == null) { //---4
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
Ⅴ 静态内部类实现
当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance()方法从而触发 ,并且 JVM 能确保 只被实例化一次。这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
2. 简单工厂(Simple Factory)
解决的问题:相似类对象的实例化过程繁琐,如果在每一个需要对象的主类中进行判断和实例化逻辑显得比较臃肿复杂,并且耦合严重。
解决的方案:对这些类抽象成一个统一接口,创建一个工厂类,主类访问工厂类获取对象,工厂类完成这个接口实现类的所有实例化逻辑。
何时使用:我们明确地计划不同条件下创建不同实例时,并且想在主类中隐藏这些实例化逻辑的时候。
实现-案例:
// 将这些类抽象成接口和实现类的形式
public interface Product {
}
public class ConcreteProduct implements Product {
}
public class ConcreteProduct1 implements Product {
}
public class ConcreteProduct2 implements Product {
}
//如果不使用简单工厂设计模式,所有需要生成这类对象的地方都需要写复杂臃肿耦合度高的逻辑判断
public class Client {
public static void main(String[] args) {
int type = 1;
Product product;
if (type == 1) {
product = new ConcreteProduct1();
} else if (type == 2) {
product = new ConcreteProduct2();
} else {
product = new ConcreteProduct();
}
// do something with the product
}
}
//使用简单工厂,所有需要生成这类实例的地方都调用工厂的createProduct方法,这样代码清晰不臃肿。如果以后新增了product,不需要在所有使用的地方加逻辑,只需要实现接口,修改工厂方法就行了,耦合程度大大降低。
public class SimpleFactory {
public Product createProduct(int type) {
if (type == 1) {
return new ConcreteProduct1();
} else if (type == 2) {
return new ConcreteProduct2();
}
return new ConcreteProduct();
}
}
public class Client {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Product product = simpleFactory.createProduct(1);
// do something with the product
}
}
3. 工厂方法(Factory Method)
解决的问题:和简单工厂一样,但是简单工厂只用一个工厂类就把所有的逻辑写了,违背职责单一原则,一旦这个类出问题所有地方都用不了了。
解决的方案:再抽象一层,形成父子结构,父类工厂定义结构,不同的子工厂去生成对应的实例,把实例化操作推迟到子类。
何时使用:我们明确地计划不同条件下创建不同实例时,并且想在主类中隐藏这些实例化逻辑的时候。
实现-案例:
//同上简单工厂的案例,将Factory拆分成 抽象工厂 和不同的具体工厂,抽象工厂调用方法,具体工厂实现方法。
//实际就是将工厂的生成实例的方法拿到子类工厂去实现,所以叫工厂方法模型。
//但是虽然职责更单一了,代码不臃肿了,也符合开闭原则单一职责原则了。却增加了更多类,增加了结构的复杂性
public abstract class Factory {
abstract public Product factoryMethod();
public void doSomething() {
Product product = factoryMethod();
// do something with the product
}
}
public class ConcreteFactory extends Factory {
public Product factoryMethod() {
return new ConcreteProduct();
}
}
public class ConcreteFactory1 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct1();
}
}
public class ConcreteFactory2 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct2();
}
}
4. 抽象工厂(Abstract Factory)
解决的问题:和工厂方法一样的问题,但是需要同时生成一个产品族的多个不同类型的产品
解决的方案:将产品族的概念抽象出来,一个工厂对应一个产品族而不是一个产品
何时使用:工厂方法的工厂-产品一一对应满足不了产品族的创建时
注:一个产品族的概念类似于(潮流的上衣、潮流的短裤、潮流的鞋子),而不是(低调的上衣、奢华的上衣、炫酷的上衣),也就是说一个ConcreteFactory建造的是同一族而不是同一类的产品,这或许对于你该如何抽象一个ConcreteFactory有帮助。
实现-案例:
public class AbstractProductA {
}
public class AbstractProductB {
}
public class ProductA1 extends AbstractProductA {
}
public class ProductA2 extends AbstractProductA {
}
public class ProductB1 extends AbstractProductB {
}
public class ProductB2 extends AbstractProductB {
}
public abstract class AbstractFactory {
abstract AbstractProductA createProductA();
abstract AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA1();
}
AbstractProductB createProductB() {
return new ProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA2();
}
AbstractProductB createProductB() {
return new ProductB2();
}
}
public class Client {
public static void main(String[] args) {
AbstractFactory abstractFactory = new ConcreteFactory1();
AbstractProductA productA = abstractFactory.createProductA();
AbstractProductB productB = abstractFactory.createProductB();
// do something with productA and productB
}
}
5. 建造者(Builder)
解决的问题:一个复杂的对象(组成相对稳定,但是每个part又比较灵活)的创建比较繁琐
解决的方案:抽象一个Builder,每个继承类给出稳定的组合,对于每个part的灵活生成由自己负责
何时使用:一些基本部件不会变,而其组合经常变化的时候。
实现-案例:
class Product {
private String partA;
private String partB;
private String partC;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
public void setPartC(String partC) {
this.partC = partC;
}
public void show() {
//显示产品的特性
}
}
abstract class Builder {
//创建产品对象
protected Product product = new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
//返回产品对象
public Product getResult() {
return product;
}
}
public class ConcreteBuilder extends Builder {
public void buildPartA() {
product.setPartA("建造 PartA");
}
public void buildPartB() {
product.setPartB("建造 PartB");
}
public void buildPartC() {
product.setPartC("建造 PartC");
}
}
class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
//产品构建与组装方法
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
public class Client {
public static void main(String[] args) {
//遇到不同类型的Part的时候用不同的ConcreteBuilder即可
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();
product.show();
}
}
6. 原型(Prototype)
解决的问题:经常使用频繁创建销毁的类对象会带来很大的开销(不是完全相同的类对象,无法通过单例解决)
解决的方案:将使用过的这类对象都存起来,需要再次使用的时候clone出来即可。
何时使用:当一个类的实例只能有几个不同状态组合中的一种,并且频繁使用的时候。
实现-案例:
public abstract class Prototype {
abstract Prototype myClone();
}
public class ConcretePrototype extends Prototype {
private String filed;
public ConcretePrototype(String filed) {
this.filed = filed;
}
@Override
Prototype myClone() {
return new ConcretePrototype(filed);
}
@Override
public String toString() {
return filed;
}
}
public class Client {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype("abc");
Prototype clone = prototype.myClone();
System.out.println(clone.toString());
}
}
参考: