美文网首页
设计模式简述

设计模式简述

作者: 小成都人 | 来源:发表于2023-07-10 12:34 被阅读0次

    设计模式分为三大类型,24种模式

    • 创建型
      • 单例模式
      • 简单工厂模式
      • 工厂方法模式
      • 抽象工厂模式
      • 建造者模式
      • 原型模式
    • 结构型
      • 代理模式
      • 适配器模式
      • 装饰器模式
      • 桥接模式
      • 组合模式
      • 享元模式
      • 外观模式
    • 行为型
      • 观察者模式
      • 模板方法模式
      • 命令模式
      • 状态模式
      • 职责链模式
      • 结束其模式
      • 中介模式
      • 访问者模式
      • 策略模式
      • 备忘录模式
      • 迭代器模式

    创建型

    • 用于对象的创建机制,提供了创建对象的最佳实践。
    • 将对象的创建与使用分离,隐藏了对象创建的细节,使客户端代码更加简洁。
    • 提供了一种标准化的方式来创建对象,从而提高了代码的复用性和可维护性。
    单例模式

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

    结构:

    • 单例类。只能创建一个实例的类
    • 访问类。使用单例类

    单例模式分为两种:

    • 饿汉式:类加载就会导致该单实例对象被创建

    • 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建

    1. 饿汉式-方式1(静态变量方式)

      /**
       * 饿汉式
       *      静态变量创建类的对象
       */
      public class Singleton {
          //私有构造方法
          private Singleton() {}
      
          //在成员位置创建该类的对象
          private static Singleton instance = new Singleton();
      
          //对外提供静态方法获取该对象
          public static Singleton getInstance() {
              return instance;
          }
      }
      

      说明:

      该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。

    1. 饿汉式-方式2(静态代码块方式)

      /**
       * 饿汉式
       *      在静态代码块中创建该类对象
       */
      public class Singleton {
      
          //私有构造方法
          private Singleton() {}
      
          //在成员位置创建该类的对象
          private static Singleton instance;
      
          static {
              instance = new Singleton();
          }
      
          //对外提供静态方法获取该对象
          public static Singleton getInstance() {
              return instance;
          }
      }
      

      <font color='red'>说明:</font>

      该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题。

    1. 懒汉式-方式1(线程不安全)

      /**
       * 懒汉式
       *  线程不安全
       */
      public class Singleton {
          //私有构造方法
          private Singleton() {}
      
          //在成员位置创建该类的对象
          private static Singleton instance;
      
          //对外提供静态方法获取该对象
          public static Singleton getInstance() {
      
              if(instance == null) {
                  instance = new Singleton();
              }
              return instance;
          }
      }
      

      <font color='red'>说明:</font>

      从上面代码我们可以看出该方式在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?当调用getInstance()方法获取Singleton类的对象的时候才创建Singleton类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题。

    1. 懒汉式-方式2(线程安全)

      /**
       * 懒汉式
       *  线程安全
       */
      public class Singleton {
          //私有构造方法
          private Singleton() {}
      
          //在成员位置创建该类的对象
          private static Singleton instance;
      
          //对外提供静态方法获取该对象
          public static synchronized Singleton getInstance() {
      
              if(instance == null) {
                  instance = new Singleton();
              }
              return instance;
          }
      }
      

      <font color='red'>说明:</font>

      该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。

    1. 懒汉式-方式3(双重检查锁)

      再来讨论一下懒汉模式中加锁的问题,对于 getInstance() 方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。由此也产生了一种新的实现模式:双重检查锁模式

      /**
       * 双重检查方式
       */
      public class Singleton { 
      
          //私有构造方法
          private Singleton() {}
      
          private static Singleton instance;
      
         //对外提供静态方法获取该对象
          public static Singleton getInstance() {
           //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
              if(instance == null) {
                  synchronized (Singleton.class) {
                      //抢到锁之后再次判断是否为null
                      if(instance == null) {
                          instance = new Singleton();
                      }
                  }
              }
              return instance;
          }
      }
      

      双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。

      要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。

      /**
       * 双重检查方式
       */
      public class Singleton {
      
          //私有构造方法
          private Singleton() {}
      
          private static volatile Singleton instance;
      
         //对外提供静态方法获取该对象
          public static Singleton getInstance() {
           //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
              if(instance == null) {
                  synchronized (Singleton.class) {
                      //抢到锁之后再次判断是否为空
                      if(instance == null) {
                          instance = new Singleton();
                      }
                  }
              }
              return instance;
          }
      }
      

      <font color="red">小结:</font>

      添加 volatile 关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。

    1. 懒汉式-方式4(静态内部类方式)

      静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。

      /**
       * 静态内部类方式
       */
      public class Singleton {
      
          //私有构造方法
          private Singleton() {}
      
          private static class SingletonHolder {
              private static final Singleton INSTANCE = new Singleton();
          }
      
          //对外提供静态方法获取该对象
          public static Singleton getInstance() {
              return SingletonHolder.INSTANCE;
          }
      }
      

      <font color='red'>说明:</font>

      第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder

      并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

      <font color="red">小结:</font>

      静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。

    1. 枚举方式

      枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。

      /**
       * 枚举方式
       */
      public enum Singleton {
          INSTANCE;
      }
      

      <font color='red'>说明:</font>

      枚举方式属于饿汉式方式。

    简单工厂模式

    介绍
    简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它提供了一个统一的接口来创建对象,但由工厂类来决定实例化哪一个类。简单工厂模式并不属于23种经典设计模式之一,但它是最简单、最常用的设计模式之一,适用于创建对象较少的情况下。

    结构
    简单工厂模式包含三个角色:

    1. 工厂(Factory): 负责创建对象的工厂类,通过传递不同的参数来创建不同的实例。
    2. 产品(Product): 抽象产品类,定义了产品的规范,描述了产品的属性和功能。
    3. 具体产品(Concrete Product): 实现了抽象产品类的具体子类。

    示例
    假设我们有一个简单的图形绘制程序,可以绘制圆形和矩形。我们使用简单工厂模式来创建不同的图形对象。

    1. 创建抽象图形类:
    // 抽象图形类
    interface Shape {
      void draw();
    }
    
    // 圆形类
    class Circle implements Shape {
      @Override
      public void draw() {
          System.out.println("绘制圆形");
      }
    }
    
    // 矩形类
    class Rectangle implements Shape {
      @Override
      public void draw() {
          System.out.println("绘制矩形");
      }
    }
    
    1. 创建简单工厂类:
    // 图形工厂类
    class ShapeFactory {
        // 创建图形对象
        public static Shape createShape(String type) {
            if (type.equalsIgnoreCase("CIRCLE")) {
                return new Circle();
            } else if (type.equalsIgnoreCase("RECTANGLE")) {
                return new Rectangle();
            }
            return null;
        }
    }
    
    
    1. 客户端使用:
    // 客户端代码
    public class Client {
        public static void main(String[] args) {
            // 创建圆形
            Shape circle = ShapeFactory.createShape("CIRCLE");
            circle.draw();
    
            // 创建矩形
            Shape rectangle = ShapeFactory.createShape("RECTANGLE");
            rectangle.draw();
        }
    }
    

    分析

    • 简单工厂模式将对象的创建和对象的使用分离,客户端不需要知道具体的产品类,只需要知道产品类的工厂即可。
    • 当需要添加新的产品时,只需修改工厂类即可,不需要修改客户端代码。

    简单工厂模式虽然简单,但它也有一些缺点:

    • 工厂类职责过重,违背了单一职责原则,一旦工厂类出现问题,整个系统都会受到影响。
    • 不符合开闭原则,每次添加新产品时都需要修改工厂类的代码,违背了开闭原则。
    工厂方法模式

    工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但让子类决定实例化哪个类。工厂方法模式将对象的实例化推迟到子类中完成,符合开闭原则,即对扩展开放,对修改关闭。

    结构
    工厂方法模式包含四个角色:

    1. 抽象产品(Product): 定义了产品的规范,描述了产品的属性和功能。
    2. 具体产品(Concrete Product): 实现了抽象产品类的具体子类。
    3. 抽象工厂(Factory): 定义了创建产品对象的工厂接口,包含了创建产品的抽象方法。
    4. 具体工厂(Concrete Factory): 实现了抽象工厂接口,负责创建具体的产品对象。

    示例
    假设我们有一个简单的图形绘制程序,可以绘制圆形和矩形。我们使用工厂方法模式来创建不同的图形对象。

    1. 创建抽象产品类:
    // 抽象图形类
    interface Shape {
        void draw();
    }
    
    // 圆形类
    class Circle implements Shape {
        @Override
        public void draw() {
            System.out.println("绘制圆形");
        }
    }
    
    // 矩形类
    class Rectangle implements Shape {
        @Override
        public void draw() {
            System.out.println("绘制矩形");
        }
    }
    
    
    1. 创建抽象工厂类:
    // 图形工厂接口
    interface ShapeFactory {
        Shape createShape();
    }
    
    // 圆形工厂类
    class CircleFactory implements ShapeFactory {
        @Override
        public Shape createShape() {
            return new Circle();
        }
    }
    
    // 矩形工厂类
    class RectangleFactory implements ShapeFactory {
        @Override
        public Shape createShape() {
            return new Rectangle();
        }
    }
    
    
    1. 客户端使用:
    // 客户端代码
    public class Client {
        public static void main(String[] args) {
            // 创建圆形工厂
            ShapeFactory circleFactory = new CircleFactory();
            // 使用圆形工厂创建圆形对象
            Shape circle = circleFactory.createShape();
            circle.draw();
    
            // 创建矩形工厂
            ShapeFactory rectangleFactory = new RectangleFactory();
            // 使用矩形工厂创建矩形对象
            Shape rectangle = rectangleFactory.createShape();
            rectangle.draw();
        }
    }
    
    

    分析
    工厂方法模式与简单工厂模式相比,将工厂类进行了抽象,每个产品都有对应的工厂类,遵循了开闭原则,但也增加了代码量。

    • 当需要添加新的产品时,只需创建对应的产品类和工厂类,无需修改原有代码。
    • 工厂方法模式符合单一职责原则,每个具体工厂类只负责创建对应的产品对象。
    • 客户端只需要知道具体工厂类即可,无需关心具体产品类的实例化过程。

    工厂方法模式更符合开闭原则,适用于产品种类较多且需要经常扩展的情况。

    抽象工厂模式

    抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式通过引入抽象工厂接口和抽象产品接口,使得客户端可以使用抽象的方式创建一系列相关的产品对象,而无需关心具体的实现类。
    结构
    抽象工厂模式包含四个角色:

    1. 抽象工厂(Abstract Factory): 声明了一组用于创建一系列产品对象的方法,每一个方法对应一种产品。
    2. 具体工厂(Concrete Factory): 实现了抽象工厂接口,负责创建一系列具体的产品对象。
    3. 抽象产品(Abstract Product): 声明了产品对象的接口,是所有具体产品类的父类。
    4. 具体产品(Concrete Product): 实现了抽象产品接口,是被创建的对象。

    示例

    假设我们有一个简单的图形绘制程序,可以绘制不同风格的按钮和文本框。我们使用抽象工厂模式来创建不同风格的按钮和文本框对象。

    1. 创建抽象产品接口:
    // 抽象按钮类
    interface Button {
        void display();
    }
    
    // 抽象文本框类
    interface TextField {
        void display();
    }
    
    1. 创建具体产品类:
    // Spring风格按钮类
    class SpringButton implements Button {
        @Override
        public void display() {
            System.out.println("显示浅绿色按钮");
        }
    }
    
    // Spring风格文本框类
    class SpringTextField implements TextField {
        @Override
        public void display() {
            System.out.println("显示浅绿色文本框");
        }
    }
    
    // Summer风格按钮类
    class SummerButton implements Button {
        @Override
        public void display() {
            System.out.println("显示浅蓝色按钮");
        }
    }
    
    // Summer风格文本框类
    class SummerTextField implements TextField {
        @Override
        public void display() {
            System.out.println("显示浅蓝色文本框");
        }
    }
    
    
    1. 创建抽象工厂接口:
    // 抽象工厂接口
    interface SkinFactory {
        Button createButton(); // 创建按钮
        TextField createTextField(); // 创建文本框
    }
    
    
    1. 创建具体工厂类:
    // Spring风格工厂类
    class SpringSkinFactory implements SkinFactory {
        @Override
        public Button createButton() {
            return new SpringButton();
        }
    
        @Override
        public TextField createTextField() {
            return new SpringTextField();
        }
    }
    
    // Summer风格工厂类
    class SummerSkinFactory implements SkinFactory {
        @Override
        public Button createButton() {
            return new SummerButton();
        }
    
        @Override
        public TextField createTextField() {
            return new SummerTextField();
        }
    }
    
    
    1. 客户端使用:
    // 客户端代码
    public class Client {
        public static void main(String[] args) {
            // 创建Spring风格工厂
            SkinFactory springFactory = new SpringSkinFactory();
            // 创建Spring风格按钮
            Button springButton = springFactory.createButton();
            // 创建Spring风格文本框
            TextField springTextField = springFactory.createTextField();
            springButton.display();
            springTextField.display();
    
            // 创建Summer风格工厂
            SkinFactory summerFactory = new SummerSkinFactory();
            // 创建Summer风格按钮
            Button summerButton = summerFactory.createButton();
            // 创建Summer风格文本框
            TextField summerTextField = summerFactory.createTextField();
            summerButton.display();
            summerTextField.display();
        }
    }
    

    分析
    抽象工厂模式将具体工厂类的创建抽象化,使得客户端可以使用抽象的方式创建一系列相关的产品对象,而无需关心具体的实现类。

    • 当需要添加新的产品族时,只需创建对应的产品接口和工厂接口,并实现具体的产品类和工厂类,无需修改原有代码。
    • 抽象工厂模式符合开闭原则,对扩展开放,对修改关闭。
    • 客户端使用抽象工厂和抽象产品接口,可以方便地替换不同的具体工厂和产品,实现了客户端与具体产品的解耦。
    建造者模式

    建造者模式(Builder Pattern)是一种创建型设计模式,用于构建复杂对象。它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
    结构

    建造者模式包含四个角色:

    1. 产品(Product): 定义了要创建的复杂对象。通常包含多个部件。
    2. 抽象建造者(Builder): 定义了创建产品各个部件的抽象方法,通常包含 buildPart1(), buildPart2() 等方法。
    3. 具体建造者(Concrete Builder): 实现了抽象建造者接口,负责构建和装配各个部件,实现具体的构建过程。
    4. 指挥者(Director): 负责调用具体建造者来创建产品对象,它并不知道具体的产品内容,只知道建造的步骤,根据传入的具体建造者对象完成产品的构建。

    示例
    假设我们要构建一份电脑,电脑包含CPU、内存、硬盘等组件。我们使用建造者模式来构建电脑对象。

    1. 创建产品类:
    // 电脑产品类
    class Computer {
        private String cpu;
        private String memory;
        private String hardDisk;
    
        public void setCpu(String cpu) {
            this.cpu = cpu;
        }
    
        public void setMemory(String memory) {
            this.memory = memory;
        }
    
        public void setHardDisk(String hardDisk) {
            this.hardDisk = hardDisk;
        }
    
        public void show() {
            System.out.println("电脑配置:");
            System.out.println("CPU:" + cpu);
            System.out.println("内存:" + memory);
            System.out.println("硬盘:" + hardDisk);
        }
    }
    
    
    1. 创建抽象建造者接口:
    // 抽象电脑建造者接口
    interface ComputerBuilder {
        void buildCpu();
        void buildMemory();
        void buildHardDisk();
        Computer buildComputer();
    }
    
    1. 创建具体建造者类:
    // 具体电脑建造者类
    class ConcreteComputerBuilder implements ComputerBuilder {
        private Computer computer = new Computer();
    
        @Override
        public void buildCpu() {
            computer.setCpu("Intel Core i7");
        }
    
        @Override
        public void buildMemory() {
            computer.setMemory("16GB DDR4");
        }
    
        @Override
        public void buildHardDisk() {
            computer.setHardDisk("1TB SSD");
        }
    
        @Override
        public Computer buildComputer() {
            return computer;
        }
    }
    
    
    1. 创建指挥者类:
    // 指挥者类
    class Director {
        private ComputerBuilder builder;
    
        public Director(ComputerBuilder builder) {
            this.builder = builder;
        }
    
        public Computer construct() {
            builder.buildCpu();
            builder.buildMemory();
            builder.buildHardDisk();
            return builder.buildComputer();
        }
    }
    
    1. 客户端使用:
    // 客户端代码
    public class Client {
        public static void main(String[] args) {
            // 创建具体建造者
            ConcreteComputerBuilder builder = new ConcreteComputerBuilder();
            // 创建指挥者
            Director director = new Director(builder);
            // 构建电脑对象
            Computer computer = director.construct();
            // 显示电脑配置
            computer.show();
        }
    }
    

    分析
    建造者模式将一个复杂对象的构建过程与它的表现分离,使得相同的构建过程可以创建不同的表示。

    1. 建造者模式使得客户端不需要知道产品内部构建的细节,只需要知道产品的建造步骤和具体建造者即可。
    2. 当需要创建复杂对象,并且对象的各个部分需要按照一定的顺序构建时,建造者模式是一个很好的选择。
    3. 建造者模式可以很容易地改变产品的内部表示,也方便扩展新的具体建造者类。
    原型模式

    原型模式(Prototype Pattern)是一种创建型设计模式,用于创建对象的一种模式。原型模式通过复制现有对象的原型来创建新的对象,而不是通过实例化类来创建对象。这种方式可以提高对象创建的性能,并且隐藏对象的创建细节。

    结构
    原型模式包含以下角色:

    1. 原型(Prototype): 声明一个克隆自身的接口。
    2. 具体原型(Concrete Prototype):实现原型接口,实现克隆自身的操作。
    3. 客户端(Client): 使用原型对象的客户端。

    示例
    假设我们有一个简单的图形类,可以表示不同形状的图形。我们使用原型模式来复制图形对象。

    1. 创建原型接口:
    // 原型接口
    interface Shape extends Cloneable {
        Shape clone(); // 克隆方法
        void draw(); // 绘制方法
    }
    
    1. 创建具体原型类:
    // 圆形类
    class Circle implements Shape {
        @Override
        public Shape clone() {
            Circle circle = null;
            try {
                circle = (Circle) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return circle;
        }
    
        @Override
        public void draw() {
            System.out.println("绘制圆形");
        }
    }
    
    // 正方形类
    class Square implements Shape {
        @Override
        public Shape clone() {
            Square square = null;
            try {
                square = (Square) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return square;
        }
    
        @Override
        public void draw() {
            System.out.println("绘制正方形");
        }
    }
    
    1. 客户端使用:
    // 客户端代码
    public class Client {
        public static void main(String[] args) {
            // 创建原型对象
            Circle circle = new Circle();
            Square square = new Square();
    
            // 克隆对象
            Shape clonedCircle = circle.clone();
            Shape clonedSquare = square.clone();
    
            // 绘制图形
            clonedCircle.draw();
            clonedSquare.draw();
        }
    }
    
    

    分析
    原型模式通过复制现有对象的原型来创建新的对象,避免了直接实例化类来创建对象的过程。原型模式适用于需要创建复杂对象,并且对象的创建过程比较耗时或者复杂的情况下。

    • 原型模式可以提高对象的创建性能,因为复制一个对象比创建一个对象的开销要小。
    • 原型模式隐藏了对象的创建细节,客户端可以直接克隆原型对象,而无需关心对象的创建过程。
    • 原型模式需要注意深拷贝和浅拷贝的问题,确保克隆对象的所有属性都是独立的。

    结构型

    • 用于解决类或对象的组合方式,以形成更大的结构。
    • 提供了一种将对象组合成更大的结构,以解决复杂性和变化的方式。
    • 通过使用不同的结构型模式,可以灵活地将类或对象组合起来,以实现不同的功能和行为。
    代理模式

    代理模式(Proxy Pattern)是一种结构型设计模式,它提供了一种代理来控制对其他对象的访问。

    ** 结构**
    代理模式包含以下角色:

    1. 抽象主题(Subject):定义了真实主题和代理主题的共同接口,客户端通过这个接口访问真实主题和代理主题。
    2. 真实主题(Real Subject): 定义了真实对象的功能,是代理模式中的实际执行对象。
    3. 代理(Proxy): 包含了对真实主题的引用,在其内部实例化了真实主题对象,可以控制对真实主题对象的访问,并且可以在调用真实主题对象之前或之后执行一些额外的操作。

    示例
    假设我们有一个文件加载器接口 FileLoader,包含了加载文件和显示文件内容的方法。我们使用代理模式来控制对文件加载器的访问。

    1. 创建抽象主题接口:
    // 抽象主题接口
    interface FileLoader {
        void loadFile(String fileName);
        void displayFile();
    }
    
    
    1. 创建真实主题类:
    // 真实主题类
    class RealFileLoader implements FileLoader {
        private String fileName;
    
        public RealFileLoader(String fileName) {
            this.fileName = fileName;
            loadFile(fileName);
        }
    
        @Override
        public void loadFile(String fileName) {
            System.out.println("加载文件:" + fileName);
        }
    
        @Override
        public void displayFile() {
            System.out.println("显示文件内容:" + fileName);
        }
    }
    
    
    1. 创建代理类:
    // 代理类
    class FileLoaderProxy implements FileLoader {
        private RealFileLoader realFileLoader;
        private String fileName;
    
        public FileLoaderProxy(String fileName) {
            this.fileName = fileName;
        }
    
        @Override
        public void loadFile(String fileName) {
            if (realFileLoader == null) {
                realFileLoader = new RealFileLoader(fileName);
            }
        }
    
        @Override
        public void displayFile() {
            if (realFileLoader != null) {
                realFileLoader.displayFile();
            } else {
                System.out.println("请先加载文件");
            }
        }
    }
    
    
    1. 客户端使用:
    // 客户端代码
    public class Client {
        public static void main(String[] args) {
            // 创建代理对象
            FileLoader proxy = new FileLoaderProxy("example.txt");
    
            // 加载并显示文件内容
            proxy.loadFile("example.txt");
            proxy.displayFile();
        }
    }
    

    分析
    代理模式通过引入代理对象来控制对真实对象的访问,可以在访问真实对象前后进行一些额外的操作,如记录日志、权限控制、缓存等。

    • 代理模式可以提高真实对象的安全性,客户端无法直接访问真实对象,必须通过代理对象来访问。
    • 代理模式可以提高真实对象的性能,通过延迟加载、缓存等方式来减少真实对象的创建和销毁次数。
    • 代理模式可以在不修改真实对象的情况下,对真实对象的功能进行扩展,如记录日志、权限控制等。

    在示例中,FileLoaderProxy 作为代理类,控制了对 RealFileLoader的访问,并在访问真实对象之前加载文件,访问真实对象之后显示文件内容。

    适配器模式

    适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户希望的另一个接口。适配器模式通常用于解决接口不兼容的问题,使得原本由于接口不匹配而无法一起工作的类可以协同工作。

    特点:

    • 适配器模式包括类适配器对象适配器两种实现方式。
    • 适配器模式通过一个适配器类将目标类和适配者类进行适配,使它们可以协同工作。
    • 适配器模式是一种补救措施,通常用于解决接口不兼容的问题。

    优点:

    • 可以让两个原本不兼容的接口协同工作。
    • 可以提高代码的复用性。
    • 可以让客户端更加灵活,可以在不修改原有代码的情况下使用新的适配者类。

    缺点:

    • 增加了系统的复杂性,引入了额外的适配器类。
    • 增加了系统的代码量。

    下面是一个简单的适配器模式的示例代码:

    目标接口(Target):

    // 目标接口
    interface Target {
        void request();
    }
    

    适配者类(Adaptee):

    // 适配者类
    class Adaptee {
        void specificRequest() {
            System.out.println("适配者中的业务代码被调用!");
        }
    }
    

    类适配器(Class Adapter):

    // 类适配器
    class ClassAdapter extends Adaptee implements Target {
        @Override
        public void request() {
            specificRequest();
        }
    }
    
    

    对象适配器(Object Adapter):

    // 对象适配器
    class ObjectAdapter implements Target {
        private Adaptee adaptee;
    
        public ObjectAdapter(Adaptee adaptee) {
            this.adaptee = adaptee;
        }
    
        @Override
        public void request() {
            adaptee.specificRequest();
        }
    }
    

    客户端代码:

    public class AdapterPatternExample {
        public static void main(String[] args) {
            // 类适配器
            Target classAdapter = new ClassAdapter();
            classAdapter.request();
    
            // 对象适配器
            Adaptee adaptee = new Adaptee();
            Target objectAdapter = new ObjectAdapter(adaptee);
            objectAdapter.request();
        }
    }
    

    在上面的示例中,Adaptee 是适配者类,Target 是目标接口。ClassAdapterObjectAdapter 是适配器类,分别使用类适配器和对象适配器两种实现方式来实现适配器模式。通过适配器类,可以让 AdapteeTarget 协同工作。

    装饰器模式

    装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向现有的对象添加新的功能,同时又不改变其结构。装饰器模式通过创建一个包装器(Wrapper)来包裹原始对象,然后在包装器中添加新的功能。

    特点:

    • 装饰器模式通过一种灵活的方式来扩展对象的功能,而不需要通过继承来实现。
    • 装饰器模式允许不断地向一个对象添加新的功能,而且可以多次添加不同的装饰器。
    • 装饰器模式可以使得各个装饰器之间相互独立,可以根据需要灵活地组合使用。

    优点:

    • 可以动态地给一个对象添加新的功能,而不需要修改其原有的代码。
    • 可以避免使用子类继承的方式导致的类爆炸问题。
    • 可以使用多个装饰器来组合使用,实现各种功能的组合。

    缺点:

    • 装饰器模式增加了系统的复杂性,引入了许多小对象和装饰器之间的关联关系。
    • 过多的装饰器会导致代码变得复杂,不易理解和维护。

    适用场景:

    • 需要在不影响其他对象的情况下,动态地给一个对象添加新的功能。
    • 需要动态地组合功能,而使用继承关系无法实现或者不方便使用继承关系的情况。
    • 下面是一个简单的装饰器模式的示例代码:

    抽象组件接口(Component):

    // 抽象组件接口
    interface Component {
        void operation();
    }
    

    具体组件(Concrete Component):

    // 具体组件
    class ConcreteComponent implements Component {
        @Override
        public void operation() {
            System.out.println("执行具体组件的操作!");
        }
    }
    

    抽象装饰器(Decorator):

    // 抽象装饰器
    abstract class Decorator implements Component {
        private Component component;
    
        public Decorator(Component component) {
            this.component = component;
        }
    
        @Override
        public void operation() {
            if (component != null) {
                component.operation();
            }
        }
    }
    

    具体装饰器(Concrete Decorator):

    // 具体装饰器
    class ConcreteDecorator extends Decorator {
        public ConcreteDecorator(Component component) {
            super(component);
        }
    
        @Override
        public void operation() {
            super.operation();
            addedBehavior();
        }
    
        private void addedBehavior() {
            System.out.println("为具体组件添加额外的行为!");
        }
    }
    

    客户端代码:

    public class DecoratorPatternExample {
        public static void main(String[] args) {
            // 创建具体组件对象
            Component component = new ConcreteComponent();
    
            // 创建装饰器对象,并传入具体组件对象
            Component decorator = new ConcreteDecorator(component);
    
            // 调用装饰器对象的方法,实际上会执行具体组件对象的方法,然后再添加额外的行为
            decorator.operation();
        }
    }
    

    在这个示例中,ConcreteComponent 是具体的组件,Decorator 是抽象装饰器,ConcreteDecorator 是具体的装饰器。通过装饰器模式,我们可以动态地给 ConcreteComponent 添加新的行为,而不需要修改 ConcreteComponent 的代码。

    桥接模式

    桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与其实现部分分离,使它们可以独立变化。桥接模式通过将抽象部分实现部分分离来实现解耦,从而提高了系统的灵活性和扩展性。

    特点:

    • 桥接模式将一个对象的抽象和实现分离开来,使它们可以独立变化。
    • 桥接模式通过聚合的方式将抽象部分和实现部分组合起来,而不是使用继承的方式,从而减少了类之间的耦合。
    • 桥接模式可以让抽象部分和实现部分可以独立地扩展,而不会相互影响。

    优点:

    • 将抽象部分和实现部分分离开来,使它们可以独立变化,提高了系统的灵活性和扩展性。
    • 可以通过组合的方式来实现不同的组合效果,增加了系统的复用性。
    • 可以在运行时动态地选择和切换实现部分,使系统更加灵活。

    缺点:

    • 增加了系统的复杂性,引入了额外的抽象和实现的类。
    • 需要对抽象部分和实现部分进行正确的设计,增加了系统的设计和开发的难度。

    适用场景:

    • 当一个对象有两个或多个维度上的变化,而且这些变化需要独立地扩展和变化时。
    • 当一个对象的抽象和实现需要独立变化时。
    • 当一个对象需要在运行时动态地选择和切换实现部分时。

    下面是一个简单的桥接模式的示例代码:

    抽象部分(Abstraction):

    // 抽象部分
    abstract class Abstraction {
        protected Implementor implementor;
    
        public Abstraction(Implementor implementor) {
            this.implementor = implementor;
        }
    
        abstract void operation();
    }
    

    具体抽象部分(Refined Abstraction):

    // 具体抽象部分
    class RefinedAbstraction extends Abstraction {
        public RefinedAbstraction(Implementor implementor) {
            super(implementor);
        }
    
        @Override
        void operation() {
            System.out.println("RefinedAbstraction operation");
            implementor.operationImpl();
        }
    }
    

    实现部分(Implementor):

    // 实现部分
    interface Implementor {
        void operationImpl();
    }
    

    具体实现部分(Concrete Implementor):

    // 具体实现部分
    class ConcreteImplementorA implements Implementor {
        @Override
        public void operationImpl() {
            System.out.println("ConcreteImplementorA operationImpl");
        }
    }
    
    class ConcreteImplementorB implements Implementor {
        @Override
        public void operationImpl() {
            System.out.println("ConcreteImplementorB operationImpl");
        }
    }
    

    客户端代码:

    public class BridgePatternExample {
        public static void main(String[] args) {
            // 创建具体的实现部分对象
            Implementor implementorA = new ConcreteImplementorA();
            Implementor implementorB = new ConcreteImplementorB();
    
            // 创建具体的抽象部分对象,并传入实现部分对象
            Abstraction abstractionA = new RefinedAbstraction(implementorA);
            Abstraction abstractionB = new RefinedAbstraction(implementorB);
    
            // 调用抽象部分对象的方法,实际上会调用实现部分对象的方法
            abstractionA.operation();
            abstractionB.operation();
        }
    }
    

    在这个示例中,Abstraction 是抽象部分,Implementor 是实现部分,RefinedAbstraction 是具体的抽象部分,ConcreteImplementorAConcreteImplementorB 是具体的实现部分。通过桥接模式,可以让抽象部分和实现部分可以独立变化,实现了抽象和实现的分离,提高了系统的灵活性和扩展性。

    组合模式

    组合模式(Composite Pattern)是一种结构型设计模式,它允许将对象组合成树形结构来表现“部分-整体”的层次结构。组合模式使得客户端可以统一对待单个对象和组合对象,从而简化了客户端的代码。

    特点:

    • 组合模式将对象组合成树形结构来表示“部分-整体”的层次结构。
    • 组合模式统一了单个对象和组合对象的接口,使得客户端可以统一对待它们。
    • 组合模式可以让客户端忽略组合对象和单个对象之间的差异,以一致的方式来处理它们。

    优点:

    • 可以使客户端统一处理单个对象和组合对象,简化了客户端的代码。
    • 可以通过递归遍历整个树形结构,实现对整个结构的统一操作。
    • 可以灵活地增加和扩展组合对象和单个对象,符合开闭原则。

    缺点:

    • 设计较为复杂,不易理解和掌握。
    • 可能会导致系统过度一般化,使得系统变得过于抽象化。

    适用场景:

    • 当需求中出现“部分-整体”的层次结构,并且希望客户端统一对待单个对象和组合对象时。
    • 当希望客户端可以忽略组合对象和单个对象之间的差异,以一致的方式来处理它们时。
    • 当希望通过递归遍历整个树形结构,实现对整个结构的统一操作时。

    下面是一个简单的组合模式的示例代码:

    抽象组件(Component):

    // 抽象组件
    interface Component {
        void operation();
    }
    

    叶子组件(Leaf):

    // 叶子组件
    class Leaf implements Component {
        private String name;
    
        public Leaf(String name) {
            this.name = name;
        }
    
        @Override
        public void operation() {
            System.out.println("Leaf " + name + " operation");
        }
    }
    

    组合组件(Composite):

    // 组合组件
    class Composite implements Component {
        private List<Component> children = new ArrayList<>();
    
        public void add(Component component) {
            children.add(component);
        }
    
        public void remove(Component component) {
            children.remove(component);
        }
    
        @Override
        public void operation() {
            for (Component component : children) {
                component.operation();
            }
        }
    }
    

    客户端代码:

    public class CompositePatternExample {
        public static void main(String[] args) {
            // 创建叶子组件对象
            Component leaf1 = new Leaf("1");
            Component leaf2 = new Leaf("2");
            Component leaf3 = new Leaf("3");
    
            // 创建组合组件对象,并添加叶子组件对象
            Composite composite = new Composite();
            composite.add(leaf1);
            composite.add(leaf2);
    
            // 调用组合组件对象的方法,实际上会递归调用叶子组件对象的方法
            composite.operation();
    
            // 添加叶子组件对象到另一个组合组件对象中
            Composite composite2 = new Composite();
            composite2.add(leaf3);
            composite2.add(composite);
    
            // 调用另一个组合组件对象的方法,实际上会递归调用两个叶子组件对象的方法
            composite2.operation();
        }
    }
    

    在这个示例中,Component 是抽象组件,Leaf 是叶子组件,Composite 是组合组件。通过组合模式,可以将多个对象组合成树形结构,使客户端可以统一处理单个对象和组合对象,从而简化了客户端的代码。

    享元模式

    享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在通过共享对象来减少内存使用和提高性能。在享元模式中,共享对象通常是不可变的,因此可以安全地在多个环境中共享。

    特点:

    • 享元模式通过共享对象来减少内存使用和提高性能。
    • 享元模式将对象分为内部状态和外部状态,内部状态是可以共享的,外部状态是不可以共享的。
    • 享元模式通常使用工厂模式来创建共享对象。

    优点:

    • 减少了对象的创建数量,节约了内存使用,提高了性能。
    • 可以使用享元工厂来维护共享对象的池,确保对象的唯一性。
    • 适用于需要大量相似对象的场景,可以减少系统的内存使用。

    缺点:

    • 增加了系统的复杂性,需要维护共享对象的池。
    • 共享对象通常是不可变的,可能会导致系统的不灵活。
    • 需要区分内部状态和外部状态,增加了系统的设计和开发的难度。

    适用场景:

    • 当系统中存在大量相似对象,且这些对象可以共享时。
    • 当需要大量创建对象,且对象的创建成本比较高时。
    • 当需要缓存对象,以提高系统性能时。

    下面是一个简单的享元模式的示例代码:

    抽象享元角色(Flyweight):

    // 抽象享元角色
    interface Shape {
        void draw(String color);
    }
    

    具体享元角色(Concrete Flyweight):

    // 具体享元角色
    class Circle implements Shape {
        private String color;
    
        public Circle() {
            // 内部状态,不可共享
        }
    
        @Override
        public void draw(String color) {
            // 外部状态,可以共享
            this.color = color;
            System.out.println("绘制一个 " + color + " 的圆形");
        }
    }
    

    享元工厂角色(Flyweight Factory):

    // 享元工厂角色
    class ShapeFactory {
        private static final Map<String, Shape> shapes = new HashMap<>();
    
        public static Shape getCircle(String color) {
            Circle circle = (Circle) shapes.get(color);
    
            if (circle == null) {
                circle = new Circle();
                shapes.put(color, circle);
            }
            return circle;
        }
    }
    

    客户端代码:

    public class FlyweightPatternExample {
        private static final String[] colors = {"红色", "绿色", "蓝色", "黄色", "黑色"};
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                Shape circle = ShapeFactory.getCircle(getRandomColor());
                circle.draw(getRandomColor());
            }
        }
    
        private static String getRandomColor() {
            return colors[(int) (Math.random() * colors.length)];
        }
    }
    

    在这个示例中,Shape 是抽象享元角色,Circle 是具体享元角色,ShapeFactory 是享元工厂角色。通过享元模式,可以减少对象的创建数量,节约内存使用,提高系统性能。

    外观模式

    行为型

    • 用于对象之间的通信,以及算法和责任的分配。
    • 定义了对象之间的交互方式,以及算法和责任的分配方式。
    • 通过使用不同的行为型模式,可以实现不同的通信和交互方式,以及算法和责任的分配方式。
    观察者模式
    模板方法模式
    命令模式
    状态模式
    职责链模式
    结束其模式
    中介模式
    访问者模式
    策略模式
    备忘录模式
    迭代器模式

    相关文章

      网友评论

          本文标题:设计模式简述

          本文链接:https://www.haomeiwen.com/subject/zypeudtx.html