23种设计模式

93 阅读11分钟

一、创建型

(一)简单工厂模式

(二)工厂方法模式

(三)抽象工厂模式

// 产品
public class Computer {

    private Board board;

    private Cpu cpu;

    public  Computer(Board board,Cpu cpu){
        this.board = board;
        this.cpu = cpu;
    }

   public void run(){
       board.run();
       cpu.run();
   }

}

// 两个产品等级结构 Board和Cpu
public interface Board {

    void run();
}

public interface Cpu {

    void run();
}

// 产品族1
public class Board1  implements Board{
    @Override
    public void run() {
        System.out.println("board1 run");
    }
}

public class Cpu1  implements Cpu{
    @Override
    public void run() {
        System.out.println("cpu1 run");
    }
}

// 产品族2
public class Board2 implements Board{
    @Override
    public void run() {
        System.out.println("board2 run");
    }
}

public class Cpu2 implements Cpu{
    @Override
    public void run() {
        System.out.println("cpu2 run");
    }
}


// 顶级工厂,定义方法
public interface ComputerFactory {

    Board makeBoard();

    Cpu makeCpu();

}

// 两个子类工厂,负责创建
public class Computer1Factory  implements ComputerFactory{
    @Override
    public Board makeBoard() {
        return new Board1();
    }

    @Override
    public Cpu makeCpu() {
        return new Cpu1();
    }
}

public class Computer2Factory implements ComputerFactory{
    @Override
    public Board makeBoard() {
        return new Board2();
    }

    @Override
    public Cpu makeCpu() {
        return new Cpu2();
    }
}


 // 测试
    public static void main(String[] args) {
        ComputerFactory f1 = new Computer1Factory();
        Board b1 = f1.makeBoard();
        Cpu c1 = f1.makeCpu();
        Computer computer1 = new Computer(b1, c1);
        computer1.run();

        ComputerFactory f2 = new Computer2Factory();
        Board b2 = f2.makeBoard();
        Cpu c2 = f2.makeCpu();
        Computer computer2 = new Computer(b2, c2);
        computer2.run();

    }
  • 增加产品族很方便,只需要添加新的产品和工厂即可。符合“开闭原则”。
// 产品族3
public class Board3  implements Board{
    @Override
    public void run() {
        System.out.println("board3 run");
    }
}

public class Cpu3  implements Cpu{
    @Override
    public void run() {
        System.out.println("cpu3 run");
    }
}


// 增加新的工厂
public class Computer3Factory implements ComputerFactory{
    @Override
    public Board makeBoard() {
        return new Board3();
    }

    @Override
    public Cpu makeCpu() {
        return new Cpu3();
    }
}
  • 增加新的产品机构等级困难,需要修改顶级工厂及所有子类工厂。不符合“开闭原则”。
// 增加产品结构等级Power ,则需要在工厂中添加新的方法,所有子类工厂都必须修改。
public interface ComputerFactory {

    Board makeBoard();

    Cpu makeCpu();
  
  Power makePower()// 
}

(四)建造者模式

(五)单例模式

  • 饿汉模式
public class Singleton {

    private Singleton(){};

    private static Singleton instance = new Singleton();

    public static Singleton getInstance(){
        return instance;
    }
}
  • 饱汉模式
// 使用双重检查
public class Singleton {

    private Singleton() { }

    private static volatile Singleton instance = null;

    public static Singleton getInstance() {
        if (null == instance) {
            synchronized (Singleton.class) {
                if (null == instance) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

// 使用嵌套类
public class Singleton {

    private Singleton() { }


    private static class Holder{
        private static  Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance() {
       return Holder.instance;
    }
}
  1. 构造方法私有,确保外部不能通过new创建对象
  2. 提供公共的静态方法

二、结构型

(一)适配器模式

将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)

  • 默认适配器

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。因此也称为单接口适配器模式。

// 适配者
public interface Adaptee {

    void method1();
    void method2();
    void method3();

}

// 默认适配器
public class DefaultAdapter  implements Adaptee{
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    @Override
    public void method3() {

    }
}


public class Target extends DefaultAdapter{

    @Override
    public void method1() {
        System.out.println("default method1");
    }

}

public class Test {
    public static void main(String[] args) {
        Target target = new Target();
        target.method1();
    }
}
  • 类适配器

适配器类实现了目标抽象类接口并继承了适配者类,并在目标抽象类的实现方法中调用所继承的适配者类的方法。

通过实现接口或者继承

public interface Target {
    void method1();
    void method2();
    void method3();
}

public class Adaptee {
    public void method1(){
        System.out.println("method1");
    }

    public void method2(){
        System.out.println("method2");
    }
}

public class Adapter extends Adaptee implements Target{
    @Override
    public void method3() {
        System.out.println("adapter method3");
    }
}

public class Test {
    public static void main(String[] args) {
        Target t = new Adapter();
        t.method1(); // 通过继承获取
        t.method2();  // 通过继承获取
        t.method3();  // 自己实现的
    }
}
  • 对象适配器

在对象适配器模式中,适配器类继承了目标抽象类并定义了一个适配者类的对象实例,在所继承的目标抽象类方法中调用适配者类的相应业务方法。

public interface Target {
    void method();
}

public class Adaptee {
    public void method(){
        System.out.println("method");
    }

}

public class Adapter  implements Target{
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee){
        this.adaptee = adaptee;
    }

    @Override
    public void method() {
        this.adaptee.method();
    }
}

    public static void main(String[] args) {
        Target t = new Adapter(new Adaptee());
        t.method();
    }

(二)代理模式

给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
public interface Subject {
    void doSome();
}
public class SubjectImpl implements Subject{
    @Override
    public void doSome() {
        System.out.println("SubjectImpl do some");
    }
}

public class Proxy implements Subject{

    private Subject target = new SubjectImpl();

    @Override
    public void doSome() {
        target.doSome();
    }
}

 public static void main(String[] args) {
        Subject proxy = new Proxy();
        proxy.doSome();
    }

动态代理

  • 动态代理是一种较为高级的代理模式,它的典型应用就是Spring AOP。
  • 在传统的代理模式中,客户端通过Proxy调用SubjectImpl类的doSome()方法,同时还在代理类中封装了其他方法,可以处理一些其他问题。
  • 如果按照这种方法使用代理模式,那么真实主题角色必须是事先已经存在的,并将其作为代理对象的内部成员属性。如果一个真实主题角色必须对应一个代理主题角色,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数,此外,如何在事先不知道真实主题角色的情况下使用代理主题角色,这都是动态代理需要解决的问题。

(三)桥接模式

将抽象部分与它的实现部分分离,使它们都可以独立地变化

多个变化维度,每个维度都抽象化,依靠具体的实现去组合,其中一个充当桥梁

 // Level充当桥梁 ,有Info Debug Warn Error 实现类
public interface Level {
    void log(String msg);
}

public class Info implements Level{
    @Override
    public void log(String msg) {
        System.out.println("info "+msg);
    }
}

public class Debug implements Level{
    @Override
    public void log(String msg) {
        System.out.println("debug "+msg);
    }
}
public class Warn implements Level{
    @Override
    public void log(String msg) {
        System.out.println("warn "+msg);
    }
}

public class Error implements Level{
    @Override
    public void log(String msg) {
        System.out.println("error "+msg);
    }
}

// Appender 的实现类,包含Level实例
public abstract class Appender {
   protected Level level;

   protected Appender(Level level){
       this.level = level;
   }
   abstract void log(String msg);
}

public class ConsoleAppender extends Appender{

    protected ConsoleAppender(Level level) {
        super(level);
    }

    @Override
    void log(String msg) {
        super.level.log(msg);
    }
}

public class DatabaseAppender extends Appender{

    protected DatabaseAppender(Level level) {
        super(level);
    }

    @Override
    void log(String msg) {
        super.level.log(msg);
    }
}

public class FileAppender extends Appender{

    protected FileAppender(Level level) {
        super(level);
    }

    @Override
    void log(String msg) {
        super.level.log(msg);
    }
}

// Appender和Level自由组合
 public static void main(String[] args) {
        ConsoleAppender consoleAppender = new ConsoleAppender(new Info());
        ConsoleAppender consoleAppender1 = new ConsoleAppender(new Error());

        FileAppender fileAppender = new FileAppender(new Info());
        DatabaseAppender databaseAppender = new DatabaseAppender(new Debug());

        String msg = "bridge pattern";
        consoleAppender.log(msg);
        consoleAppender1.log(msg);
        fileAppender.log(msg);
        databaseAppender.log(msg);
    }
  • 分离抽象接口及其实现部分。
  • 桥接模式有时类似于多继承方案,但是多继承方案违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差,而且多继承结构中类的个数非常庞大,桥接模式是比多继承方案更好的解决方法。
  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
  • 实现细节对客户透明,可以对用户隐藏实现细节。

(四)装饰模式

动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。

适合给一个对象增加多个小功能。

// 目标类,有两个实现类BlueDrink  RedDrink
public interface Drinks {
    String getDesc();
    BigDecimal getPrice();
}


public class BlueDrink implements Drinks{
    @Override
    public String getDesc() {
        return "蓝色饮料";
    }

    @Override
    public BigDecimal getPrice() {
        return BigDecimal.valueOf(20);
    }
}
public class RedDrink implements Drinks{
    @Override
    public String getDesc() {
        return "红色饮料";
    }

    @Override
    public BigDecimal getPrice() {
        return BigDecimal.valueOf(10);
    }
}

// 装饰器,需要实现目标类的所有方法
public abstract class DrinkDecorator implements Drinks{

 // 这里需要传入目标类,方便对齐进行增强
    protected Drinks drinks;

    protected DrinkDecorator(Drinks drinks){
        this.drinks = drinks;
    }

}

// 三个装饰器
public class CoffeeDecorator extends DrinkDecorator{


    public CoffeeDecorator(Drinks drinks) {
        super(drinks);
    }

    @Override
    public String getDesc() {
        return super.drinks.getDesc()+" + 咖啡";
    }

    @Override
    public BigDecimal getPrice() {
        return new BigDecimal(2).add(super.drinks.getPrice());
    }
}
public class SaltDecorator extends DrinkDecorator{


    public SaltDecorator(Drinks drinks) {
        super(drinks);
    }

    @Override
    public String getDesc() {
        return super.drinks.getDesc()+" + 盐";
    }

    @Override
    public BigDecimal getPrice() {
        return new BigDecimal(0.5).add(super.drinks.getPrice());
    }
}

public class SugurDecorator extends DrinkDecorator{


    public SugurDecorator(Drinks drinks) {
        super(drinks);
    }

    @Override
    public String getDesc() {
        return super.drinks.getDesc()+" + 糖";
    }

    @Override
    public BigDecimal getPrice() {
        return new BigDecimal(1).add(super.drinks.getPrice());
    }
}

public class Test {
    public static void main(String[] args) {

        Drinks drinks = new CoffeeDecorator(new SugurDecorator(new RedDrink()));

        System.out.println(drinks.getDesc()+" "+drinks.getPrice());

    }
}
  • 与继承关系相比,关联关系的主要优势在于不会破坏类的封装性,而且继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。在软件开发阶段,关联关系虽然不会比继承关系减少编码量,但是到了软件维护阶段,由于关联关系使系统具有较好的松耦合性,因此使得系统更加容易维护。当然,关联关系的缺点是比继承关系要创建更多的对象。
  • 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动态地给一个对象附加更多的责任。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展。

(五)门面模式

外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。

public interface Shape {
    void draw();
}


public class CircleShape implements Shape{
    @Override
    public void draw() {
        System.out.println("Circle::draw");
    }
}
public class RectangleShape implements Shape{
    @Override
    public void draw() {
        System.out.println("Retangle::draw");
    }
}

public class ShapeFacade {

    private Shape circleShape;
    private Shape retanglesShape;

    public ShapeFacade() {
        this.circleShape = new CircleShape();
        this.retanglesShape = new RectangleShape();
    }

    public void drawCircle(){
        circleShape.draw();
    }

    public void drawRetangle(){
        retanglesShape.draw();
    }
}

 public static void main(String[] args) {
        ShapeFacade facade = new ShapeFacade();
        facade.drawCircle();
        facade.drawRetangle();
    }

门面模式的优点

  • 对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入外观模式,客户代码将变得很简单,与之关联的对象也很少。
  • 实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。
  • 降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
  • 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类。

门面模式的缺点

  • 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

(六)享元模式

(七)组合模式

三、行为型

(一)策略模式

完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务

public interface Strategy {
    void draw();
}
public class RedPen implements Strategy{
    @Override
    public void draw() {
        System.out.println("redpen::draw");
    }
}
public class BluePen implements Strategy{
    @Override
    public void draw() {
        System.out.println("bluepen::draw");
    }
}
public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeDraw(){
        this.strategy.draw();
    }

}

    public static void main(String[] args) {
        Context context = new Context(new RedPen());
        context.executeDraw();

        context = new Context(new BluePen());
        context.executeDraw();
    }

(二)观察者模式

定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
// 被观察者
public class Subject {
    private int state;
    // 保存所有的观察者
    private List<Observer> observers = new ArrayList<>();

    public void registerObserver(Observer observer){
        this.observers.add(observer);
    }

    public void setState(int state){
        this.state = state;
        this.notifyObserver();
    }

    private void notifyObserver(){
        for (Observer observer : observers) {
            observer.onUpdate();
        }
    }
}

// 观察者
public abstract class Observer {
    protected Subject subject;

    abstract void onUpdate();
}

public class Observer1 extends Observer{
    public Observer1(Subject subject) {
        this.subject = subject;
        this.subject.registerObserver(this);
    }

    @Override
    public void onUpdate() {
        System.out.println("observer1 on update");
    }
}

public class Observer2 extends Observer{

    public Observer2(Subject subject) {
        this.subject = subject;
        this.subject.registerObserver(this);
    }

    @Override
    public void onUpdate() {
        System.out.println("observer2 on update");
    }
}


    public static void main(String[] args) {
        Subject subject = new Subject();
        Observer observer1 = new Observer1(subject);
        Observer observer2 = new Observer2(subject);

        subject.setState(2);
    }

观察者模式的核心是,一定有个地方存放了所有的观察者,当事件发生的时候,遍历观察者,调用他们的回调函数。

(三)责任链模式

通常建立一个单向链表,调动头部就可以,后面会自动流转起来。

public class Context {

    private Boolean isNewUser;

    private Boolean isLimit;

    private Boolean isStock;

    public Context(Boolean isNewUser, Boolean isLimit, Boolean isStock) {
        this.isNewUser = isNewUser;
        this.isLimit = isLimit;
        this.isStock = isStock;
    }
}

public abstract class RuleHandler {

    protected RuleHandler successor;

    public abstract void apply(Context context);

    public void setSuccessor(RuleHandler successor){
        this.successor = successor;
    }

    public RuleHandler getSuccessor(){
        return this.successor;
    }
}

public class NewUserHandler extends RuleHandler{

    @Override
    public void apply(Context context) {
        if (context.getNewUser()){
            // do sth
            if (null != this.getSuccessor()){
                this.getSuccessor().apply(context);
            }
        }else {
            throw  new RuntimeException("不是新用户");
        }
    }
}

public class LimitUserHandler extends RuleHandler{

    @Override
    public void apply(Context context) {
        if (context.getLimit()){
            // do sth
            if (null != this.getSuccessor()){
                this.getSuccessor().apply(context);
            }
        }else {
            throw  new RuntimeException("不在限定范围内");
        }
    }
}

public class StockUserHandler extends RuleHandler{

    @Override
    public void apply(Context context) {
        if (context.getStock()){
            // do sth
            if (null != this.getSuccessor()){
                this.getSuccessor().apply(context);
            }
        }else {
            throw  new RuntimeException("库存不足");
        }
    }
}

   public static void main(String[] args) {
        Context context = new Context(true,true,false);
        NewUserHandler newUserHandler = new NewUserHandler();
        LimitUserHandler limitUserHandler = new LimitUserHandler();
        StockUserHandler stockUserHandler = new StockUserHandler();
        limitUserHandler.setSuccessor(stockUserHandler);
        newUserHandler.setSuccessor(limitUserHandler);

        newUserHandler.apply(context);
    }

(四)模板方法模式

在含有继承结构的代码中,模板方法非常好用

public abstract class Template {

    // 最后需要执行的方法
    public void execute() {
        start();
        run();
        end();
    }

 // 留着子类去实现
    protected abstract void start();

    protected abstract void run();

    protected abstract void end();

}

public class ConcreteTemplate extends Template{
    @Override
    protected void start() {
        System.out.println("ConcreteTemplate start");
    }

    @Override
    protected void run() {
        System.out.println("ConcreteTemplate run");
    }

    @Override
    protected void end() {
        System.out.println("ConcreteTemplate end");
    }
}

  public static void main(String[] args) {
        ConcreteTemplate template = new ConcreteTemplate();
        template.execute();
    }

(五)状态模式

(六)命令模式

(七)解释器模式

(八)迭代器模式

(九)备忘录模式

(十)访问者模式

(十一)中介者模式