【设计模式】 - 结构型模式(Structural Pattern)

结构型模式关注点“怎样组合对象/类

  • 类结构型模式:关心类的组合,由多个类可以组合成一个更大的(继承)
  • 对象结构型模式:关心类与对象的组合,通过关联关系在一个类中定义另一个类的实例对象(组合)
  • 根据“合成复用原则”,尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。

适配器模式 (Adapter Pattern)

将一个接口转换成客户希望的另一个接口。
适配器模式使接口不兼容的那些类可以一起工作。

适配器模式分为类结构型模式(继承)和对象结构型模式(组合)两种,前者(继承)类之间的耦合度比后
者高,且要求使用人了解现有组件库中的相关组件的内部结构,所以应用相对较少些。

角色

  • 目标(Target)接口:可以是抽象类或接口。客户希望直接用的接口。
  • 适配者(Adaptee)类:隐藏的转换接口。
  • 适配器(Adapter)类:转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口。
    在这里插入图片描述

实例

类结构适配器

系统原有功能代码:

// 播放接口和其实现
public interface Player {
    String play();
}

public class MoviePlayer implements Player {
    @Override
    public String play() {
        System.out.println("播放视频");
        return "你好 ";
    }
}


//翻译接口和其实现
public interface Translator {
    String translator(String content);
}

public class Zh_HPTranslator implements Translator {
    @Override
    public String translator(String content) {
        if ("你好".equals(content)) {
            return "扣你吉瓦";
        }
        return "*******";
    }
}

类适配器通过继承和实现接口的方式

在这里插入图片描述

public class JP_MoviePlayerClassAdapter extends Zh_HPTranslator implements Player {
	
	// 真实播放对象
    private MoviePlayer moviePlayer;
	
	//注入播放对象
    public JP_MoviePlayerClassAdapter(MoviePlayer moviePlayer) {
        this.moviePlayer = moviePlayer;
    }

    @Override
    public String play() {
        // 真实播放接口
        String content = moviePlayer.play();
        // 字幕调用集成而来的翻译接口
        return this.translator(content);
    }
}

对象结构适配器

对象结构适配器实际上就是使用合成复用原则
在这里插入图片描述

public class JP_MoviePlayerObjectAdapter implements Player {

    // 翻译对象
    private Zh_HPTranslator translator = new Zh_HPTranslator();

    // 播放对象
    private Player player;

    // 构造注入
    public JP_MoviePlayerObjectAdapter(Player player) {
        this.player = player;
    }

    @Override
    public String play() {
        // 具体的播放实现
        String content = player.play();
        // 翻译
        return translator.translator(content);
    }
}

应用思考

  • Tomcat如何将Request流转为标准Request
  • Spring AOP中的AdvisorAdapter是什么
  • Spring MVC中经典的HandlerAdapter是什么
  • SpringBoot 中 WebMvcConfigurerAdapter为什么存在又取消


桥接模式 (Bridge Pattern)

在现实生活中,某些类具有两个或多个维度的变化,如图形既可按形状分,又可按颜色分。如何设计类似于 Photoshop 这样的软件,能画不同形状和不同颜色的图形呢?如果用继承方式,m 种形状和n 种颜色的图形就有 m×n 种,不但对应的子类很多,而且扩展困难。

将抽象与实现解耦,使两者都可以独立变化

桥接将继承转为关联,降低类之间的耦合度,减少代码量

角色

  • 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用
  • 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法
  • 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用
  • 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现
    在这里插入图片描述

实例

定义手机抽象类,将“销售渠道”抽取出来,作为手机的属性。

// 构造略
public abstract class AbstractPhone {

    // 渠道信息 - [渠道抽取 ,外部独立拓展]
    public AbstractSale sale;

    public void showInfo() {
        System.out.println(sale.type + "       " + sale.price);
    }

}

// 构造略
public abstract class AbstractSale {

    public String type;
    // 价格
    public Integer price;

}

销售各渠道实现

// 线下实现
public class OfflineSale extends AbstractSale {

    public OfflineSale(String type, Integer price) {
        super(type, price);
    }

}
// 线上实现
public class OnlineSale extends AbstractSale {
    public OnlineSale(String a, int i) {
        super(a, i);
    }
}

手机实现

public class Iphone extends AbstractPhone {
    public Iphone(AbstractSale a) {
        super(a);
    }
}

进行测试

public class BridgeStart {
    public static void main(String[] args) {
        // 测试Iphone
        Iphone iphone = new Iphone(new OnlineSale("线上", 1000));
        Iphone iphone1 = new Iphone(new OfflineSale("线下", 1300));
        iphone.showInfo();
        iphone1.showInfo();
    }
}

结果展示

线上       1000
线下       1300

使用场景

  • 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时
  • 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增
    加时
  • 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活
    性时

装饰器模式(Decorator/Wrapper Pattern)

  • 向一个现有的对象添加新的功能,同时又不改变其结构。
  • 创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

角色

  • 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(ConcreteComponent)角色: 实现抽象构件,通过装饰角色为其添加一些职责
  • 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能
  • 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
    在这里插入图片描述

实例

抽象构件

public interface Tiktok {
    void tiktok();
}

抽象构件的实现

public class MyTiktok implements Tiktok {
    @Override
    public void tiktok() {
        System.out.println("开启了直播");
    }
}

直播想开启美颜效果,在不改变接口方法的情况下扩展新的功能。
抽象装饰器

public interface TiktokDecorator {
    void enable();
}

装饰器实现

public class MyTiktokDecorator implements TiktokDecorator {

    // 被装饰的对象
    private MyTiktok tiktok;

    public MyTiktokDecorator(MyTiktok myTiktok) {
        this.tiktok = myTiktok;
    }

    @Override
    public void enable() {
        System.out.println("开启了美颜功能!");
        tiktok.tiktok();
    }
}

测试&输出

public class WrapperStart {
    public static void main(String[] args) {
        MyTiktokDecorator decorator = new MyTiktokDecorator(new MyTiktok());
        decorator.enable();
    }
}
开启了美颜功能!
开启了直播

应用思考

  • SpringSession中如何进行session与redis关联?HttpRequestWrapper
  • MyBatisPlus提取了QueryWrapper,这是什么?
  • Spring中的BeanWrapper是做什么?
  • Spring Webflux中的 WebHandlerDecorator?

代理模式(Proxy Pattern)

代理模式给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
对象结构型模式(静态代理)

静态代理略过

角色

  • Subject: 抽象主体角色(抽象类或接口)
  • Proxy: 代理主体角色(代理对象类)
  • RealSubject: 真实主体角色(被代理对象类)

动态代理

JDK动态代理

通过jdkProxy类生成了一个代理对象,然后执行了被代理对象中的方法。要求被代理对象必须有接口。

使用的是上述Tiktok接口MyTiktok实现类

public class JdkProxyStart {
    public static void main(String[] args) {
        // 被代理对象
        Tiktok myTiktok = new MyTiktok();
        // 获取代理对象
        Tiktok o = (Tiktok) Proxy.newProxyInstance(
                // 被代理类的加载器
                myTiktok.getClass().getClassLoader(),
                // 被代理类的接口
                myTiktok.getClass().getInterfaces(),
                // InvocationHandler  简化匿名内部类
                (proxy, method, params) -> {
                    // 增强处理
                    System.out.println("开启美颜");
                    // 返回值
                    Object result = method.invoke(myTiktok, params);
                    // 后置增强
                    System.out.println("开启DJ");
                    return result;
                }
        );
        // 代理对象执行
        o.tiktok();
    }
}

打印结果

开启美颜
开启了直播
开启DJ
CGLIB动态代理

CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,织入横切逻辑。

CGLIB 底层:采用 ASM 字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。

Tiktok类

public class Tiktok {
    public void tiktok() {
        System.out.println("开启了直播");
    }
}

实现方法拦截器

public class MyInterceptor extends Tiktok implements MethodInterceptor {
    /**
     * @param o           代理对象
     * @param method      目标方法
     * @param objects     入参
     * @param methodProxy 代理方法
     * @return 方法返回值
     * @throws Throwable 异常
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开启美颜效果");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("开启DJ");
        return result;
    }
}

测试类

public class CglibProxyStart {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        // 父类
        enhancer.setSuperclass(Tiktok.class);
        // 回调增强
        enhancer.setCallback(new MyInterceptor());
        // 获取代理对象
        Tiktok tiktok = (Tiktok) enhancer.create();
        // 代理对象执行目标防范
        tiktok.tiktok();
    }
}

使用场景

  • MyBatis的mapper到底是什么?怎么生成的?
    • MapperProxy
  • Seata的DataSourceProxy是什么?
  • DruidDataSource存在的Proxy模式

组合模式(Composite Pattern)

把一组相似的对象当作一个单一的对象。如:树形菜单。
在这里插入图片描述
结构如下,省略相关测试。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Menu {
    private String id;
    private String name;
    private List<Menu> children;

    // 添加菜单的方法
    public void addChildren(Menu menu) {
        children.add(menu);
    }

    // 递归遍历
    public void printMenu() {
        System.out.println("id = " + id + "name = " + name);
        if (children.size() > 0) {
            for (Menu menu : children) {
                menu.printMenu();
            }
        }
    }
}

外观模式(Facade Pattern)

又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。

在这里插入图片描述

实例略过

应用场景
  • JAVA 的三层开发模式。
  • 分布式系统的网关。
  • Tomcat源码中的RequestFacade。

享元模式(Flyweight Pattern)

  • 享元模式(Flyweight Pattern),运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。

  • 在享元模式中可以共享的相同内容称为内部状态,而那些需要外部环境来设置的不能共享的内容称为外部状态,由于区分了内部状态和外部状态,因此,可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。

  • 在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池用于存储具有相同内部状态的享元对象。

角色

  • Flyweight: 抽象享元类
  • ConcreteFlyweight: 具体享元类
  • UnsharedConcreteFlyweight: 非共享具体享元类
  • FlyweightFactory: 享元工厂类

在这里插入图片描述

实例

查票接口

public interface ITicket {
    void showInfo(String bunk);
}

实现类

public class TrainTicket implements ITicket {
    // 内部状态
    private String from;
    private String to;
    private int price;

    public TrainTicket(String from, String to) {
        this.from = from;
        this.to = to;
    }

    @Override
    public void showInfo(String bunk) {
        this.price = new Random().nextInt(500);
        System.out.println(String.format("%s->%s: %s 价格:%s 元", this.from, this.to, bunk, this.price));
    }
}

工厂
维护了一个享元池,存储具有相同内部状态的享元对象。

public class TicketFactory {
    // 池化
    private static Map<String, ITicket> sTicketPool = new ConcurrentHashMap<>();
    //获取票价
    public static ITicket queryTicket(String from, String to) {
        String key = from + "->" + to;
        if (TicketFactory.sTicketPool.containsKey(key)) {
            System.out.println("使用缓存:" + key);
            return TicketFactory.sTicketPool.get(key);
        }
        System.out.println("首次查询,创建对象:" + key);
        TrainTicket ticket = new TrainTicket(from, to);
        TicketFactory.sTicketPool.put(key, ticket);
        return ticket;
    }
}

测试并输出
可以看到第二次直接走了缓存,提高了效率。

public class FlyweightStart {
    public static void main(String[] args) {
        ITicket iTicket = TicketFactory.queryTicket("深圳北", "潮汕");
        iTicket.showInfo("硬座");

        ITicket iTicket1 = TicketFactory.queryTicket("深圳北", "潮汕");
        iTicket1.showInfo("软座");
    }
}
首次查询,创建对象:深圳北->潮汕
深圳北->潮汕: 硬座 价格:280 元
使用缓存:深圳北->潮汕
深圳北->潮汕: 软座 价格:454 元

应用场景

  • 经典的池化技术

过滤器模式(Filter Pattern)

使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。结合多个标准来获得单一标准。

过滤器是较为常用的设计模式之一。可以配合责任链模式,进行链式过滤

实现

POJO Persion

// 注解略
public class Person {
    private String name; // 姓名
    private String gender; // 性别
    private String marital; // 婚姻情况
}

过滤器接口及其实现

public interface Filter<T> {
    List<T> filter(List<T> persons);
}

/**
 * 过滤器实现
 */
public class MaleFilter implements Filter<Person> {

    @Override
    public List<Person> filter(List<Person> persons) {

        List<Person> result = new ArrayList<>();
        for (Person person : persons) {
            if ("MALE".equalsIgnoreCase(person.getGender())) {
                result.add(person);
            }
        }
        return result;
    }
}

准备测试数据并输出

public class FilterStart {
    public static void main(String[] args) {
        List<Person> list = Arrays.asList(
                new Person("霍一", "FEMALE", "MARRIED"),
                new Person("邓二", "MALE", "MARRIED"),
                new Person("张三", "MALE", "SINGLE"),
                new Person("李四", "FEMALE", "MARRIED"),
                new Person("王五", "MALE", "SINGLE"),
                new Person("赵六", "FEMALE", "SINGLE"),
                new Person("孙七", "MALE", "SINGLE"),
                new Person("罗八", "MALE", "MARRIED"),
                new Person("刘九", "FEMALE", "SINGLE"),
                new Person("史十", "FEMALE", "SINGLE")
        );

        // 打印出所有男性的信息
        System.out.println("---------------------所有男性---------------------");
        List<Person> maleList = new MaleFilter().filter(list);

        if (maleList != null && maleList.size() > 0) {
            for (Person person : maleList) {
                System.out.println(person);
            }
        }
    }
}
---------------------所有男性---------------------
Person(name=邓二, gender=MALE, marital=MARRIED)
Person(name=张三, gender=MALE, marital=SINGLE)
Person(name=王五, gender=MALE, marital=SINGLE)
Person(name=孙七, gender=MALE, marital=SINGLE)
Person(name=罗八, gender=MALE, marital=MARRIED)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java设计模式是一组经过实践验证的面向对象设计原则和模式,可以帮助开发人员解决常见的软件设计问题。下面是常见的23种设计模式: 1. 创建型模式(Creational Patterns): - 工厂方法模式(Factory Method Pattern) - 抽象工厂模式(Abstract Factory Pattern) - 单例模式(Singleton Pattern) - 原型模式(Prototype Pattern) - 建造者模式(Builder Pattern) 2. 结构型模式Structural Patterns): - 适配器模式(Adapter Pattern) - 桥接模式(Bridge Pattern) - 组合模式(Composite Pattern) - 装饰器模式(Decorator Pattern) - 外观模式(Facade Pattern) - 享元模式(Flyweight Pattern) - 代理模式(Proxy Pattern) 3. 行为型模式(Behavioral Patterns): - 责任链模式(Chain of Responsibility Pattern) - 命令模式(Command Pattern) - 解释器模式(Interpreter Pattern) - 迭代器模式(Iterator Pattern) - 中介者模式(Mediator Pattern) - 备忘录模式(Memento Pattern) - 观察者模式(Observer Pattern) - 状态模式(State Pattern) - 策略模式(Strategy Pattern) - 模板方法模式(Template Method Pattern) - 访问者模式(Visitor Pattern) 4. 并发型模式(Concurrency Patterns): - 保护性暂停模式(Guarded Suspension Pattern) - 生产者-消费者模式(Producer-Consumer Pattern) - 读写锁模式(Read-Write Lock Pattern) - 信号量模式(Semaphore Pattern) - 线程池模式(Thread Pool Pattern) 这些设计模式可以根据问题的特点和需求来选择使用,它们提供了一些可复用的解决方案,有助于开发高质量、可维护且易于扩展的软件系统。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总在寒冷清秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值