7大结构型设计模式

本文详细介绍了软件工程中的几种结构型设计模式,包括适配器模式在SpringMVC和JDBC中的应用,桥接模式的作用与JDBC源码剖析,装饰者模式的动态扩展功能,组合模式的层次结构,外观模式简化接口,享元模式的内存优化,以及代理模式(Cglib和JDK代理)的实现。
摘要由CSDN通过智能技术生成

结构性设计模式是软件工程中常用的一类设计模式。

作用:主要用于处理类或对象之间的组合以实现更灵活、可扩展和可维护的代码结构。

这些模式通常涉及到类和对象之间的静态组合关系,以及如何将它们组织在一起以满足特定的设计目标。

结构型模式有:

 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

适配器模式

文章地址:

适配器模式已经在SpringMVC中的源码实现-CSDN博客

总结:我调用适配器类,适配器类进行内部转换,返回给我一个想要的类

桥接模式

桥接模式以及在JDBC源码剖析-CSDN博客

总结:将抽象部分与实现部分分离,使它们可以独立变化。

装饰者模式

前提总结:动态地给一个对象添加一些额外的职责。

介绍

1、定义
装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则

//1、接口
public interface Coffee {
    double getCost();
    String getDescription();
}
​
//2、具体咖啡类
public class SimpleCoffee implements Coffee {
​
    @Override
    public double getCost() {
        return 1.0;
    }
​
    @Override
    public String getDescription() {
        return "Simple coffee";
    }
}
​
//3、装饰者类
public abstract class CoffeeDecorator implements Coffee {
​
    protected Coffee decoratedCoffee;
​
    public CoffeeDecorator(Coffee decoratedCoffee) {
        this.decoratedCoffee = decoratedCoffee;
    }
​
    public double getCost() {
        return decoratedCoffee.getCost();
    }
​
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }
}
public class MilkDecorator extends CoffeeDecorator {
​
    public MilkDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }
​
    public double getCost() {
        return super.getCost() + 0.5;
    }
​
    public String getDescription() {
        return super.getDescription() + ", with milk";
    }
}
​
​
//4、测试demo
Coffee simpleCoffee = new SimpleCoffee();
System.out.println("Cost: " + simpleCoffee.getCost() + ", Description: " + simpleCoffee.getDescription());
​
Coffee coffeeWithMilk = new MilkDecorator(simpleCoffee);
System.out.println("Cost: " + coffeeWithMilk.getCost() + ", Description: " + coffeeWithMilk.getDescription());
 

装饰者模式在JDK中使用

在JavaIO结构中,FilterInputStream就是一个装饰者

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("example.txt"));这里就是装饰者模式的应用,有点类似套娃。

总结

装饰者模式是一种结构型设计模式,它允许你动态地将责任附加到对象上。这种模式可以通过创建一个包装类来包裹原始类,在不改变原始类接口的情况下,给其添加新的功能。

优点
  1. 灵活性:装饰者模式通过嵌套组合的方式,可以在运行时动态地添加、移除或修改对象的行为,提供了很高的灵活性。

  2. 避免继承的缺点:相比使用继承来扩展对象功能,装饰者模式避免了类爆炸问题,使得代码更加灵活、可维护和可扩展。

  3. 单一职责原则:每个装饰者类都专注于一个特定的功能,符合单一职责原则,从而使得系统更易于理解和维护。

缺点
  1. 复杂性:过度使用装饰者模式可能会导致大量的小类,增加代码复杂性和理解难度。

  2. 顺序敏感:装饰者模式中的装饰顺序很重要,如果顺序错误可能导致意外结果,需要特别注意。

适用场景
  1. 当需要在不影响其他对象的情况下,动态地、递归地给对象添加功能时,可以使用装饰者模式。

  2. 当需要动态地撤销功能时,该模式同样适用。

总的来说,装饰者模式在需要灵活地扩展对象功能、避免类爆炸以及保持单一职责原则时非常有用。然而,在实际使用时,应权衡利弊,避免过度复杂化设计。

组合模式

简单总结:组合模式是一种设计模式,它允许你将对象组合成树形结构,从而形成“部分-整体”的层次结构。每个组件都可以执行一些操作,并且这些操作可以通过递归的方式应用于组合的整个结构。

介绍

1、组合模式又称部分整体模式,它创建了对象组的树型结构,将对象组合成树状结构,以表示“整体 - 部分” 的层次关系。
2、组合模式依据树型结构来组合对象,用来表示部分以及整体层次
3、属于结构者模式
4、组合模式使得用户对单个对象和组合对象的访问具有一致性。即(组合能让客户以一致的方式处理个别对象以及组合对象)

示例

import java.util.ArrayList;
import java.util.List;
​
// 组件接口
interface Component {
    void operation();
}
​
// 叶子节点类
class Leaf implements Component {
    @Override
    public void operation() {
        System.out.println("Leaf operation");
    }
}
​
// 组合节点类
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() {
        System.out.println("Composite operation");
        for (Component component : children) {
            component.operation();
        }
    }
}
​
public class Main {
    public static void main(String[] args) {
        // 创建叶子节点
        Leaf leaf1 = new Leaf();
        Leaf leaf2 = new Leaf();
​
        // 创建组合节点
        Composite composite = new Composite();
        composite.add(leaf1);
        composite.add(leaf2);
​
        // 调用操作
        composite.operation();
    }
}
​

说明:

1、Component:这是组合中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理Component子部件,Component可以是抽象类或接口
​
2、Leaf:在组合中表示叶子节点,叶子节点没有子节点
​
3、Composite:非叶子节点,用于存储子部件,在Component 接口中实现 子部件的相关操作。比如增加、删除....

组合模式在JDK中使用

HashMap中的使用。

这里是Map接口(类似上述的Component接口)

这里是HashMap的内部类,类似上述的Leaf节点

这里是HashMap类,类似上述Composite

总结

个人理解:

组合模式确实就是将复合组件(Composite)和叶子组件(Leaf)组合起来,以实现一些功能。

在组合模式中,复合组件可以包含叶子组件,也可以包含其他复合组件,从而形成一个树形结构。

这种树形结构使得客户端可以统一地对待单个组件和组合组件,而不必关心它们的具体类型。客户端可以通过统一的接口来访问组合中的所有组件,从而简化了客户端的代码。

注意事项和细节

1、简化客户端操作。 客户只需要面对一致的对象,而不需要考虑整体部分或者叶子节点的问题。

2、具有较强的扩展性。 当需要修改组合对象时,只需要调整内部的层次关系,客户端不需要做出任何改动。

3、方便创建出复杂的层次结构。 客户不需要了解内部的组成细节,容易添加节点或叶子从而创建出复杂的树型结构

4、需要遍历组织机构,或处理对象具有树型结构时,非常适合组合模式

5、需要较高的抽象性。 如果节点和叶子有很多差异性的话,不适合组合模式。

优点
  1. 简化客户端代码:客户端可以一致地处理单个对象和组合对象,不需要区分它们的具体类型,从而简化客户端代码。

  2. 灵活性:可以通过组合不同的对象来构建复杂的层次结构,同时能够方便地增加新的组件或改变组件的结构。

  3. 统一接口:叶子节点和组合节点都实现了相同的接口,使得客户端可以统一调用操作,无需关心具体实现。

  4. 容易扩展:可以很容易地添加新的组件类,无需修改现有的代码。

缺点
  1. 限制组件类型:由于所有组件都需要实现相同的接口,可能会限制组件类型的多样性。

  2. 增加复杂性:引入组合模式会增加系统的复杂性,特别是在处理递归结构时需要小心设计,避免出现死循环或性能问题。

  3. 不适合所有情况:并不是所有的场景都适合使用组合模式,特别是当组件间的层次关系比较简单或不固定时,可能会显得过度复杂。

小结

组合模式在处理整体 - 部分结构的问题上具有明显的优点,但在某些情况下也需要权衡其复杂性和适用性。

外观模式

介绍

1、外观模式又称过程模式。
外观模式为子系统中的一组接口提供了一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
​
2、外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关注子系统中的内部细节。

外观模式中的角色:

1、外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当的子系统对象。
​
2、调用者(Client):外观接口的调用者
​
3、子系统的集合:指模块或子系统,处理Facade 对象指派的任务,他是功能的提供者。

实例

下面是一个简单的家庭影院系统作为案例。在这个案例中,外观模式可以隐藏复杂的子系统,并提供一个简单的接口给客户端。

家庭影院外观 HomeTheaterFacade 封装了投影仪、音响和屏幕等子系统的操作,为客户端提供了简单的接口。客户端只需与外观类交互,而不需要了解内部子系统的复杂操作。

// 子系统类:投影仪
class Projector {
    public void on() {
        System.out.println("投影仪打开");
    }
​
    public void off() {
        System.out.println("投影仪关闭");
    }
}
​
// 子系统类:音响
class AudioSystem {
    public void on() {
        System.out.println("音响打开");
    }
​
    public void off() {
        System.out.println("音响关闭");
    }
}
​
// 子系统类:屏幕
class Screen {
    public void up() {
        System.out.println("屏幕上升");
    }
​
    public void down() {
        System.out.println("屏幕下降");
    }
}
​
// 外观类:家庭影院外观
class HomeTheaterFacade {
    private Projector projector;
    private AudioSystem audioSystem;
    private Screen screen;
​
    public HomeTheaterFacade(Projector projector, AudioSystem audioSystem, Screen screen) {
        this.projector = projector;
        this.audioSystem = audioSystem;
        this.screen = screen;
    }
​
    public void watchMovie() {
        System.out.println("准备观影...");
        projector.on();
        audioSystem.on();
        screen.down();
    }
​
    public void endMovie() {
        System.out.println("结束观影...");
        projector.off();
        audioSystem.off();
        screen.up();
    }
}
​
// 客户端类
public class Client {
    public static void main(String[] args) {
        Projector projector = new Projector();
        AudioSystem audioSystem = new AudioSystem();
        Screen screen = new Screen();
​
        HomeTheaterFacade homeTheater = new HomeTheaterFacade(projector, audioSystem, screen);
        homeTheater.watchMovie();
        System.out.println("一段时间后...");
        homeTheater.endMovie();
    }
}

外观模式在Mybatis框架的使用

上述就是在Mybatis框架中的使用。

1、子系统类:
DefaultObjectFactory类、DefaultObjectWrapperFactory类、DefaultReflectorFactory类
2、外观类:
Configuration类

外观模式像一个集合子系统类,为什么叫外观模式呢?

外观模式的主要目的是提供一个简化接口,用于访问系统中一组复杂的子系统。外观模式通过创建一个高层接口(外观类),封装了子系统中的一组接口,从而隐藏了子系统的复杂性,并提供了一个更简单的接口给客户端使用。

虽然外观模式中的外观类看起来像是集合了多个子系统的功能,但它们的核心不在于集合,而在于简化接口。外观模式的外观类不负责实现功能,而是将请求转发给子系统中相应的对象,让子系统来完成实际的工作。

因此,外观模式的核心特点可以总结为:

  1. 简化接口:外观类提供了一个简化的接口,隐藏了子系统的复杂性,使得客户端不需要了解子系统的具体实现细节。

  2. 封装子系统:外观类封装了子系统中的一组接口,通过外观类访问子系统,客户端不需要直接与子系统的组件进行交互。

  3. 解耦:外观模式将客户端与子系统之间的耦合度降低到最低程度,客户端只需要与外观类进行交互,而不需要了解子系统的内部结构。

因此,尽管外观模式中的外观类看起来像是集合了多个子系统的功能,但其主要作用是提供一个简化接口,隐藏复杂性,降低耦合度,而不是简单地将多个类集合在一起形成一个新的集合类。

总结

个人理解:

外观模式理解为一个主类(外观类)集成了多个子类(子系统),并通过外观类来调用这些子类完成特定功能的过程

外观模式是一种结构型设计模式,旨在为复杂系统提供一个简单统一的接口,从而隐藏系统的复杂性,使客户端与系统之间的交互变得更加简单和直观。

优点:

  1. 简化接口:外观模式提供了一个简单的接口,隐藏了子系统的复杂性,使客户端使用起来更加方便。

  2. 解耦:通过外观模式,客户端与子系统之间的耦合度降低,客户端不需要直接与多个子系统进行交互,只需与外观类交互即可。

  3. 更好的封装性:外观模式将子系统的细节封装在内部,对客户端隐藏了具体实现细节,提高了系统的安全性和稳定性。

缺点:

  1. 不符合开闭原则:当系统中的子系统发生变化时,可能需要修改外观类,违反了开闭原则。

  2. 可能造成性能问题:在某些情况下,外观类可能会变得过于庞大,导致性能问题。

  3. 可能引入不必要的复杂性:如果系统本身并不复杂,引入外观模式可能会增加代码复杂性。

总的来说,外观模式适合用于简化复杂系统的接口,提高系统的易用性和灵活性。在设计时需要权衡好利弊,确保外观模式的引入能够带来实际的好处。

享元模式

“享元”是中国古代的一种度量衡单位,用于衡量铜器时代的重量和容量

介绍

1、享元模式又称蝇量模式。 运用共享技术有效地支持大量细粒度对象(系统中存在许多具有相似性质和功能的小型对象)

2、常用于系统底层开发,解决系统的性能问题,像数据库连接池,里面都是创建好的链接对象,里面都是已创建好的连接对象,在这些连接对象中。

3、能够解决重复对象内存浪费问题。 当系统中有大量相似对象,需要缓冲池时,不需要新建对象,可以从缓冲池中拿。 -》降低系统内存,提高效率

4、享元模式经典使用场景:池技术。 String常量池、数据库连接池、缓冲池等等

实例

import java.util.HashMap;
import java.util.Map;
​
// 享元工厂
class FlyweightFactory {
    private static final Map<String, Flyweight> flyweights = new HashMap<>();
​
    public static Flyweight getFlyweight(String key) {
        if (!flyweights.containsKey(key)) {
            flyweights.put(key, new ConcreteFlyweight());
        }
        return flyweights.get(key);
    }
}
​
// 享元接口
interface Flyweight {
    void operation();
}
​
// 具体享元
class ConcreteFlyweight implements Flyweight {
    @Override
    public void operation() {
        System.out.println("具体享元的操作");
    }
}
​
// 客户端
public class Client {
    public static void main(String[] args) {
        Flyweight flyweight1 = FlyweightFactory.getFlyweight("key1");
        flyweight1.operation();
​
        Flyweight flyweight2 = FlyweightFactory.getFlyweight("key2");
        flyweight2.operation();
    }
}
​

代码分析

前提:
1、享元模式提出了两个要求:细粒度和共享对象。这里涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态。
    1)"大量细粒度对象"指的是系统中存在许多具有相似性质和功能的小型对象,这些对象通常具有较小的内存占用和较简单的结构。
    2)共享对象指的是被多个客户端或实例共同引用或共享的对象。这种共享可以带来一些好处,例如减少内存占用、提高性能和降低系统复杂度
    
2、内部状态:对象共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变
3、外部状态:对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态
​
代码分析
1、FlyweightFactory :享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象方法
2、Flyweight:抽象的享元对象,他是产品的抽象类,同时定义出对象的外部状态和内部状态
3、ConcreteFlyweight:具体享元对象,是具体的产品类,实现抽象角色定义的业务

享元模式在JDK-Integer的应用源码分析

       Integer x = Integer.valueOf(127);   
        Integer y = new Integer(127);
        Integer z = Integer.valueOf(127);
        Integer w = new Integer(127);
        System.out.println(x.equals(y));//true
        System.out.println(x == y);//false
        System.out.println(x == z);//true
        System.out.println(y == w);//false

小结:

1、在valueOf方法中,先判断是否在IntegerCache中,如果不在,就创建新的Integer(new),否则,就直接从缓存池中返回

2、这里使用到了享元模式

总结

个人理解: 一种生成重复对象的工厂,但是它给的如果是相同内容的对象,就是引用。 ​ ​ 享元模式确实是一种用于避免重复创建相同对象的设计模式。

当使用享元模式时,如果请求创建的对象已经存在,工厂会返回对现有对象的引用,而不是创建一个新的对象,从而节省内存和提高效率。这种方式确保了相同内容的对象在系统中只有一个实例,并且多个客户端使用同一对象的引用。

优点:

  1. 减少内存占用:通过共享对象的方式,减少了大量相似对象的内存占用。

  2. 提高性能:由于共享对象,可以减少创建和销毁对象的开销,提高系统性能。

缺点:

  1. 需要对内部状态和外部状态进行区分和管理,增加了系统复杂度。

  2. 如果共享对象过多,可能会导致系统的维护困难。

代理模式(重点)

代理模式以及静态代理、JDK代理、Cglib代理的实现-CSDN博客

一般在面试中会考察SpringAOP使用的Cglib动态代理和JDK代理的区别?

是一种通过代理某一个类,并增强其功能的设计模式。

  • 27
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值