# 结构型模式

作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
相关代码参考: 设计模式代码仓库 (opens new window)


# 代理模式

代理模式是一种结构型设计模式,其核心思想是:为其他对象提供一种代理,以控制对这个对象的访问

# 模式定义与角色

代理模式主要包含以下三个角色:

  1. 抽象主题(Subject) 定义代理类和真实主题类的公共接口,可以是接口或抽象类。
  2. 真实主题(Real Subject) 也称“被代理类”,负责实现真实的业务逻辑。
  3. 代理主题(Proxy) 内部持有真实主题的引用,负责控制访问,并可增强功能。

当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过也给代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。

# 代理模式的实现

代理类的生成方式不同,分为:

  • 静态代理:代理类由开发者编写,编译期确定。
  • 动态代理:运行时动态生成代理类,常见的实现有 JDK 动态代理 和 CGLIB 动态代理。

# 1. 静态代理

抽象主题

public interface Person {

    void findLove();
}
1
2
3
4

真实主题

@Data
public class ZhangSan implements Person {

    @Override
    public void findLove() {
        System.out.println("寻到真爱, 要求女");
    }
}
1
2
3
4
5
6
7
8

代理主题角色

public class ZhangSanFather implements Person{

    private ZhangSan zhangSan;

    @Override
    public void findLove() {
        System.out.println("替儿子物色对象");
        zhangSan.findLove();
        System.out.println("张三笑嘻嘻");
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 2. 动态代理 jdk实现

动态代理有很多API, 先用基于JDK的动态代理对代码改造, 实现一个中介角色, 毕竟不是每一个征婚人都是老登儿子

抽象主题

public interface Person {

    void findLove();
}
1
2
3
4

真实主题

@Data
public class ZhangSan implements Person {

    @Override
    public void findLove() {
        System.out.println("寻到真爱, 要求女");
    }
}
1
2
3
4
5
6
7
8

代理主题角色

public class MeiPo implements InvocationHandler {

    private Person target;

    public Person getInstance(Person target) {
        this.target = target;
        Class<? extends Person> clazz = target.getClass();
        return (Person) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("收到征婚请求, 收集女性单身角色");
    }

    private void after() {
        System.out.println("双方同意, 结婚");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

测试:

public class Test {

    public static void main(String[] args) {
        MeiPo meiPo = new MeiPo();
        Person zs = meiPo.getInstance(new ZhangSan());
        zs.findLove();
    }
}
1
2
3
4
5
6
7
8

# 3. 动态代理 cglib实现

真实主题

public class Customer {

    public void findLove() {
        System.out.println("儿子要求:肤白貌美大长腿");
    }
}
1
2
3
4
5
6

代理主题

public class CglibMeiPo implements MethodInterceptor {

    public Object getInstance(Class<?> clazz) throws Exception {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object obj = methodProxy.invokeSuper(o,objects);
        after();
        return obj;
    }

    private void before(){
        System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
        System.out.println("开始物色");
    }

    private void after(){
        System.out.println("OK的话,准备办事");
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 门面模式

又叫做外观模式, 提供了一个统一的接口, 用来访问子系统中的一群接口, 主要特点是定义一个高层接口让子系统更容易使用。

# 模式定义与角色

  1. Facade(门面类):客户端访问子系统的唯一入口。
  2. 子系统(SubSystem):实现具体功能,Facade 调用它们完成工作。

# 门面模式的实现

子系统角色

public class SubSystemA {
    public void doA() {
        System.out.println("doing A stuff");
    }
}

public class SubSystemB {
    public void doB() {
        System.out.println("doing B stuff");
    }
}

public class SubSystemC {
    public void doC() {
        System.out.println("doing C stuff");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

门面系统

public class Facade {
    private SubSystemA a = new SubSystemA();
    private SubSystemB b = new SubSystemB();
    private SubSystemC c = new SubSystemC();

    // 对外接口
    public void doA() {
        this.a.doA();
    }

    public void doB() {
        this.b.doB();
    }

    public void doC() {
        this.c.doC();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 装饰器模式

又称包装模式,在不改变原有对象结构的基础上,动态地扩展对象功能。比继承更灵活,适用于功能扩展频繁变化的场景。

# 模式定义与角色

  1. Component(抽象组件):定义对象的接口,是被装饰对象的统一规范。

  2. ConcreteComponent(具体组件):实现 Component 接口,表示被装饰的原始对象。

  3. Decorator(抽象装饰器):实现 Component 接口,持有一个 Component 对象引用,定义通用的装饰逻辑结构。

  4. ConcreteDecorator(具体装饰器):继承 Decorator,实现具体的扩展功能。

# 装饰器模式的实现

以饮品咖啡为例

  1. 抽象组件(如果只定义行为规范,优先使用接口;若需共享部分默认实现,再使用抽象类。)

    public interface Drink {
    
        String getDes();
    
        Double cost();
    }
    
    1
    2
    3
    4
    5
    6
  2. 具体组件

    public class CoffeeDrink implements Drink {
    
        @Override
        public String getDes() {
            return "coffee";
        }
    
        @Override
        public Double cost() {
            return 1.0;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  3. 抽象装饰器

    public abstract class CoffeeDecorator implements Drink {
    
        private Drink drink;
    
        public CoffeeDecorator(Drink drink) {
            this.drink = drink;
        }
    
        @Override
        public String getDes() {
            return this.drink.getDes();
        }
    
        @Override
        public Double cost() {
            return this.drink.cost();
        }
    
        protected abstract String doSomething();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
  4. 具体装饰器

    public class CoffeeMilkDecorator extends CoffeeDecorator {
    
        public CoffeeMilkDecorator(Drink drink) {
            super(drink);
        }
    
        @Override
        protected String doSomething() {
            return "";
        }
    
        @Override
        public Double cost() {
            return super.cost() + 1;
        }
    
    
        @Override
        public String getDes() {
            return super.getDes() + " 加牛奶";
        }
    }
    
    public class CoffeeSugarDecorator extends CoffeeDecorator {
    
        public CoffeeSugarDecorator(Drink drink) {
            super(drink);
        }
    
        @Override
        public String getDes() {
            return super.getDes() + " 加糖";
        }
    
        @Override
        public Double cost() {
            return super.cost() + 1;
        }
    
        @Override
        protected String doSomething() {
            return "";
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
  5. 测试

    public class Test {
    
        public static void main(String[] args) {
            Drink drink = new CoffeeDrink();
            drink = new CoffeeMilkDecorator(drink);
            drink = new CoffeeSugarDecorator(drink);
    
            System.out.println(drink.cost());
            System.out.println(drink.getDes());
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

# 享元模式

享元模式是一种结构型设计模式,适用于当系统中存在大量对象时,为减少内存开销而复用对象的场景。它通过将对象的状态划分为内部状态(Intrinsic State)与 外部状态(Extrinsic State),将不可变的内部状态共享存储,避免重复创建,实现对象复用,达到提升系统性能的目的。

享元模式可以看作是一种缓存池(Object Pool)机制,其核心思想是在工厂方法(Factory Method)的基础上加入了对象缓存功能。

# 模式定义与角色

  1. Flyweight(抽象享元角色) 定义享元对象的接口,规定内部状态与外部状态的操作方式。

  2. ConcreteFlyweight(具体享元角色) 实现 Flyweight 接口,封装内部状态,不应包含会改变共享状态的行为。

  3. FlyweightFactory(享元工厂) 管理享元对象的创建与缓存,确保相同内部状态的对象只创建一次。

# 享元模式的实现

  1. 抽象享元角色

    public interface Font {
    
        void display(String character, int size);
    }
    
    1
    2
    3
    4
  2. 具体享元角色

    public class ConcreteFont implements Font {
    
        private final String fontName; // 内部状态:字体名称
    
        public ConcreteFont(String fontName) {
            this.fontName = fontName;
        }
    
        @Override
        public void display(String character, int size) {
            System.out.println("字符:" + character + " 使用字体:" + fontName + ",字号:" + size);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  3. 享元工厂

    public class FontFactory {
    
        private static final Map<String, Font> fontPool = new HashMap<>();
    
        public static Font getFont(String fontName) {
            if (!fontPool.containsKey(fontName)) {
                fontPool.put(fontName, new ConcreteFont(fontName));
            }
            return fontPool.get(fontName);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  4. 测试案例

    public class Test {
    
        public static void main(String[] args) {
            Font font1 = FontFactory.getFont("宋体");
            Font font2 = FontFactory.getFont("宋体");
            Font font3 = FontFactory.getFont("黑体");
    
            font1.display("你", 12);
            font2.display("好", 14);
            font3.display("!", 16);
    
            System.out.println("font1 和 font2 是同一个对象吗?" + (font1 == font2)); // true
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

# 组合模式

组合模式(Composite Pattern),又称为整体-部分模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

# 模式定义与角色

  1. 抽象构件(Component):定义组合中对象的接口,可以是抽象类或接口,提供公共操作。
  2. 叶子构件(Leaf):叶子节点对象,表示树的最小单元,无子节点。
  3. 容器构件(Composite):容器节点,维护子节点集合,实现对子节点的添加、删除等操作。

# 组合模式的实现

透明组合模式: 透明组合模式是把所有公共方法都定义在component中, 客户端不需要区分树 叶结点, 具备完全一致的接口。

  1. 抽象根节点

    public abstract class FileComponent {
    
        protected String name;
    
        public FileComponent(String name) {
            this.name = name;
        }
    
        public abstract void display();
    
        // 以下是透明组合模式的关键:所有方法都放在抽象类中
        public void add(FileComponent component) {
            throw new UnsupportedOperationException("不支持添加操作");
        }
    
        public void remove(FileComponent component) {
            throw new UnsupportedOperationException("不支持移除操作");
        }
    
        public List<FileComponent> getChildren() {
            throw new UnsupportedOperationException("不支持获取子节点");
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
  2. 叶子节点

    public class FileLeaf extends FileComponent {
    
        public FileLeaf(String name) {
            super(name);
        }
    
        @Override
        public void display() {
            System.out.println("文件: " + name);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  3. 树枝节点

    public class Folder extends FileComponent {
    
        private List<FileComponent> children = new ArrayList<>();
    
        public Folder(String name) {
            super(name);
        }
    
        @Override
        public void display() {
            System.out.println("文件夹: " + name);
            for (FileComponent child : children) {
                child.display();
            }
        }
    
        @Override
        public void add(FileComponent component) {
            children.add(component);
        }
    
        @Override
        public void remove(FileComponent component) {
            children.remove(component);
        }
    
        @Override
        public List<FileComponent> getChildren() {
            return children;
        }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
  4. 测试

    public class Test {
    
        public static void main(String[] args) {
            FileComponent root = new Folder("根目录");
    
            FileComponent doc = new Folder("文档");
            FileComponent pic = new Folder("图片");
    
            FileComponent file1 = new FileLeaf("简历.docx");
            FileComponent file2 = new FileLeaf("说明.pdf");
            FileComponent image1 = new FileLeaf("照片.jpg");
    
            doc.add(file1);
            doc.add(file2);
            pic.add(image1);
    
            root.add(doc);
            root.add(pic);
    
            root.display();
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

安全组合模式, 只规定系统各个层次的最基础的一致行为, 把组合(树结点)本身的方法放到自身当中。

  1. 抽象根节点

    public interface FileComponent {
        void display();
    }
    
    1
    2
    3
  2. 叶子节点

    public class FileLeaf implements FileComponent {
    
        private String name;
    
        public FileLeaf(String name) {
            this.name = name;
        }
    
        @Override
        public void display() {
            System.out.println("文件: " + name);
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  3. 树枝节点

    public class Folder implements FileComponent {
        private String name;
        private List<FileComponent> children = new ArrayList<>();
    
        public Folder(String name) {
            this.name = name;
        }
    
        public void add(FileComponent component) {
            children.add(component);
        }
    
        public void remove(FileComponent component) {
            children.remove(component);
        }
    
        public List<FileComponent> getChildren() {
            return children;
        }
    
        @Override
        public void display() {
            System.out.println("文件夹: " + name);
            for (FileComponent child : children) {
                child.display();
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
  4. 测试

    public class Test {
        public static void main(String[] args) {
            Folder root = new Folder("根目录");
            Folder doc = new Folder("文档");
            Folder pic = new Folder("图片");
    
            FileLeaf file1 = new FileLeaf("简历.docx");
            FileLeaf file2 = new FileLeaf("说明.pdf");
            FileLeaf image1 = new FileLeaf("照片.jpg");
    
            doc.add(file1);
            doc.add(file2);
            pic.add(image1);
    
            root.add(doc);
            root.add(pic);
    
            root.display();
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

# 适配器模式

适配器模式又叫做变压器模式, 功能是将一个类的接口变成客户端所期望的另一接口, 从而使接口不匹配的2个类可以一起工作。

# 模式定义与角色

  1. 目标角色(Target): 我们期望的接口
  2. 源角色(Adaptee): 存在于系统中, 内容满足客户需求但接口不匹配的接口实例
  3. 适配器(Adapter): 将源角色转换为目标角色的类实例

# 适配器形式

  1. 类适配器

    通过继承实现适配器功能, 让Adapter实现Target接口, 并继承Adaptee

    1. 源角色

      public class Adaptee {
      
          public void oldMethod() {
              System.out.println("Adaptee: 旧接口中的方法");
          }
      }
      
      1
      2
      3
      4
      5
      6
    2. 目标角色

      public interface Target {
      
          void newMethod();
      }
      
      1
      2
      3
      4
    3. 适配器

      public class Adapter extends Adaptee implements Target{
      
          @Override
          public void newMethod() {
              System.out.println("Adapter 将 oldMethod 转为 newMethod");
              oldMethod();
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
    4. 测试

      public class Test {
      
          public static void main(String[] args) {
              Adapter adapter = new Adapter();
              adapter.newMethod();
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
  2. 对象适配器

    通过组合来实现适配器的功能, 让Adapter实现Target接口, 然后内部持有Adaptee实例, 在Target接口方法内部进行转换

    更改适配器实现

    public class Adapter implements Target {
    
        private Adaptee adaptee;
        public Adapter(Adaptee adaptee) {
            this.adaptee = adaptee;
        }
    
        @Override
        public void newMethod() {
            System.out.println("Adapter 将 oldMethod 转为 newMethod");
            adaptee.oldMethod();
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  3. 接口适配器

    主要是解决接口方法过多, 如果直接实现接口, 类会多出许多空实现的方法

    1. 源角色

      public class Adaptee extends Adapter {
      
          @Override
          public void method1() {
              System.out.println("实现 method1");
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
    2. 目标角色

      public interface Target {
      
          void method1();
      
          void method2();
      
          void method3();
      
          void method4();
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
    3. 适配器(利用抽象类实现默认方法 子类实现自己需要的方法)

      public abstract class Adapter implements Target {
      
          @Override
          public void method1() {
      
          }
      
          @Override
          public void method2() {
      
          }
      
          @Override
          public void method3() {
      
          }
      
          @Override
          public void method4() {
      
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22

# 桥接模式

将抽象部分与它的具体实现部分分离, 使其可以独立的变化, 通过组合的方式建立两个类之间的联系, 类似于多继承但是维持了类的单一职责原则

# 模式定义与角色

  1. 抽象: 该类持有一个对实现角色的引用, 抽象角色的方法需要实现角色自己实现
  2. 修正抽象: 抽象的具体实现, 对抽象的方法进行完善和扩展
  3. 实现: 确定实现维度的基本操作, 提供给抽象使用
  4. 具体实现: 实现的具体实现

# 桥接模式的实现

  1. 实现层

    public interface OperatingSystem {
        void run();
    }
    
    1
    2
    3
  2. 具体实现

    public class IOS implements OperatingSystem {
        @Override
        public void run() {
            System.out.println("运行 iOS 系统");
        }
    }
    
    public class AndroidOS implements OperatingSystem {
        @Override
        public void run() {
            System.out.println("运行 Android 系统");
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
  3. 抽象

    public abstract class Phone {
    
        protected OperatingSystem operatingSystem;
    
        public Phone(OperatingSystem operatingSystem) {
            this.operatingSystem = operatingSystem;
        }
    
        public abstract void start();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  4. 修正抽象

    public class XiaoMiPhone extends Phone {
    
        public XiaoMiPhone(OperatingSystem operatingSystem) {
            super(operatingSystem);
        }
    
        @Override
        public void start() {
            System.out.print("小米手机 -> ");
            operatingSystem.run();
        }
    }
    
    public class HuaWeiPhone extends Phone {
    
        public HuaWeiPhone(OperatingSystem operatingSystem) {
            super(operatingSystem);
        }
    
        @Override
        public void start() {
            System.out.print("华为抽象 -> ");
            operatingSystem.run();
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
  5. 测试

    public class Test {
    
        public static void main(String[] args) {
            Phone phone1 = new XiaoMiPhone(new AndroidOS());
            phone1.start();  // 输出:小米手机 -> 运行 Android 系统
    
            Phone phone2 = new HuaWeiPhone(new IOS());
            phone2.start();  // 输出:华为手机 -> 运行 iOS 系统
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10