JB1-8-设计模式(二)

Java道经第1卷 - 第8阶 - 设计模式(二)


传送门:JB1-8-设计模式(一)
传送门:JB1-8-设计模式(二)
传送门:JB1-8-设计模式(三)

S06. 结构型设计模式

E01. 适配器设计模式

心法:适配器模式,全称 Adaptor Pattern,也叫包装器模式,其核心是将一个接口转换为客户端所期待的接口,从而使两个接口不兼容的类可以在一起工作。

优点:增强程序的可扩展性,此模式下,用户可以随意扩展程序的功能,却不需要修改原来的接口。

缺点:当系统中大量使用适配器模式时,可能会造成结构混乱,此时更建议直接对系统进行重构。

场景

  • 当内存卡和笔记本接口不兼容时,使用读卡器就是一种适配器模式的解决方案。
  • 驱动技术大多使用的也是适配器模式,比如 JDBC 技术,其底层使用的就是适配器模式。

武技:测试适配器模式

  1. 开发原接口类:
package adapter;

/** 原接口:接口方法内容设计不够优化 */
public interface UserService {
    void c();

    void r();

    void u();

    void d();
}
  1. 开发新接口类:
package adapter;

/** 新接口:与原接口不兼容 */
public interface UserServicePro {
    void insert();

    void select();

    void update();

    void delete();
}
  1. 实现新接口类:
package adapter;

/** 新接口实现类 */
public class UserServiceImpl implements UserServicePro {

    @Override
    public void insert() {
        System.out.println("升级版的添加方法");
    }

    @Override
    public void select() {
        System.out.println("升级版的查询方法");
    }

    @Override
    public void update() {
        System.out.println("升级版的修改方法");
    }

    @Override
    public void delete() {
        System.out.println("升级版的删除方法");
    }
}
  1. 开发适配器类:
package adapter;

/** 适配器类:用于将新接口兼容到原接口*/
public class Adapter implements UserService {

    private final UserServicePro userServicePro;

    public Adapter(UserServicePro userServicePro) {
        this.userServicePro = userServicePro;
    }

    @Override
    public void c() {
        userServicePro.insert();
    }

    @Override
    public void r() {
        userServicePro.select();
    }

    @Override
    public void u() {
        userServicePro.update();
    }

    @Override
    public void d() {
        userServicePro.delete();
    }
}
  1. 开发测试类:
package adapter;

public class AdapterTest {
    @Test
    public void testAdapter() {
        UserService userService = new Adapter(new UserServiceImpl());
        // 调用的还是原来的方法,不过底层使用的已经是新版本的具体实现了
        userService.c();
        userService.r();
        userService.u();
        userService.d();
    }
}

E02. 桥接设计模式

心法:桥接模式,全称 Bridge Pattern,也叫柄体模式,也叫接口模式,其核心是用于将抽象部分与它的实现部分进行解耦,使它们都可以独立地变化。

场景:假设开发了一个手机抽象类,包含华为和小米两种品牌和高中低三种配置:

  • 则需要设计低配小米,中配小米,高配小米,低配华为等共 M * N 种不同的实现类。
  • 使用桥接模式,可以将手机配置剥离出去成为独立的接口,然后在手机品牌的实现类中进行引入。
  • 此时只需设计两种手机品牌的实现类和3种手机配置的实现类,共计 M + N 个实现类即可。

此模式下,相当于将品牌的选择和配置的组合方式,交给调用方来选择。

武技:测试桥接模式

  1. 开发手机配置的接口:
package bridge;

/** 手机配置接口 */
public interface PhoneConfig {
    /** 手机颜色 */
    String getColor();

    /** 手机运存 */
    Integer getRam();

    /** 手机存储 */
    Integer getRom();
}
  1. 开发手机低配置实现类:
package bridge;

/** @author 周航宇 */
public class LowPhoneConfig implements PhoneConfig {

    @Override
    public String getColor() {
        return "低配白";
    }

    @Override
    public Integer getRam() {
        return 8;
    }

    @Override
    public Integer getRom() {
        return 64;
    }
}
  1. 开发手机中配置实现类:
package bridge;

/** @author 周航宇 */
public class MiddlePhoneConfig implements PhoneConfig {

    @Override
    public String getColor() {
        return "中配黑";
    }

    @Override
    public Integer getRam() {
        return 16;
    }

    @Override
    public Integer getRom() {
        return 128;
    }
}
  1. 开发手机高配置实现类:
package bridge;

/** @author 周航宇 */
public class HighPhoneConfig implements PhoneConfig {

    @Override
    public String getColor() {
        return "高配红";
    }

    @Override
    public Integer getRam() {
        return 32;
    }

    @Override
    public Integer getRom() {
        return 256;
    }
}
  1. 开发手机抽象类:
package bridge;

/** 手机抽象类 */
public abstract class Phone {

    /** 手机的各种配置被剥离出去,并在此进行引入 */
    protected PhoneConfig phoneConfig;

    /** 抽象类的构造器只能由子类来调用 */
    public Phone(PhoneConfig phoneConfig) {
        this.phoneConfig = phoneConfig;
    }

    /** 获取手机的完整信息:具体实现由子类来决定 */
    public abstract String getInfo();
}
  1. 开发华为手机类:
package bridge;

/** @author 周航宇 */
public class HuaWeiPhone extends Phone {

    public HuaWeiPhone(PhoneConfig phoneConfig) {
        // 将手机的配置传递给父类构造器
        super(phoneConfig);
    }

    @Override
    public String getInfo() {
        return String.format("华为手机信息:[%s,运存%dG,存储%dG]",
                phoneConfig.getColor(),
                phoneConfig.getRam(),
                phoneConfig.getRom());
    }
}
  1. 开发小米手机类:
package bridge;

/** @author 周航宇 */
public class XiaoMiPhone extends Phone {

    public XiaoMiPhone(PhoneConfig phoneConfig) {
        // 将手机的配置传递给父类构造器
        super(phoneConfig);
    }

    @Override
    public String getInfo() {
        return String.format("小米手机信息:[%s,运存%dG,存储%dG]",
                phoneConfig.getColor(),
                phoneConfig.getRam(),
                phoneConfig.getRom());
    }
}
  1. 开发测试类:
package bridge;

/** @author 周航宇 */
public class BridgeTest {
    @Test
    public void testBridge() {
        // 创建华为手机实例,自定义配置
        HuaWeiPhone lowHuaWeiPhone = new HuaWeiPhone(new LowPhoneConfig());
        System.out.println("低配-" + lowHuaWeiPhone.getInfo());
        HuaWeiPhone middleHuaWeiPhone = new HuaWeiPhone(new MiddlePhoneConfig());
        System.out.println("中配-" + middleHuaWeiPhone.getInfo());
        HuaWeiPhone highHuaWeiPhone = new HuaWeiPhone(new HighPhoneConfig());
        System.out.println("高配-" + highHuaWeiPhone.getInfo());
        // 创建小米手机实例,自定义配置
        XiaoMiPhone lowXiaoMiPhone = new XiaoMiPhone(new LowPhoneConfig());
        System.out.println("低配-" + lowXiaoMiPhone.getInfo());
        XiaoMiPhone middleXiaoMiPhone = new XiaoMiPhone(new MiddlePhoneConfig());
        System.out.println("中配-" + middleXiaoMiPhone.getInfo());
        XiaoMiPhone highXiaoMiPhone = new XiaoMiPhone(new HighPhoneConfig());
        System.out.println("高配-" + highXiaoMiPhone.getInfo());
    }
}

E03. 过滤器设计模式

心法:过滤器模式,全称 Filter Pattern,也叫标准模式。

过滤器模式允许用户使用不同标准来过滤一组对象,比如 MyBatisGenerator 工具的 SQL 拼装,就是使用的该模式。

武技:测试过滤器设计模式

  1. 开发过滤实体类:
package filter.entity;

/** @author 周航宇 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private String name;
    private Integer age;
    private Integer gender;
}
  1. 开发过滤接口:
package filter;

/** @author 周航宇 */
public interface Criteria {
    /**
     * 过滤方法
     * @param students 被过滤的集合
     * @return 过滤后,满足条件的集合
     */
    Set<Student> filter(Set<Student> students);
}
  1. 开发过滤接口实现类:
package filter;

/** @author 周航宇 */
public class CriteriaAge implements Criteria {

    private final Integer age;

    public CriteriaAge(Integer age) {
        this.age = age;
    }

    @Override
    public Set<Student> filter(Set<Student> students) {
        Set<Student> result = new HashSet<>();
        // 将所有符合指定年龄条件的集合项添加到result集合
        for (Student student : students) {
            if (Objects.equals(student.getAge(), this.age)) {
                result.add(student);
            }
        }
        return result;
    }
}
  1. 开发过滤接口实现类:
package filter;

/** @author 周航宇 */
public class CriteriaGender implements Criteria {

    private final Integer gender;

    public CriteriaGender(Integer gender) {
        this.gender = gender;
    }

    @Override
    public Set<Student> filter(Set<Student> students) {
        Set<Student> result = new HashSet<>();
        // 将所有符合指定性别条件的集合项添加到result集合
        for (Student student : students) {
            if (Objects.equals(student.getGender(), this.gender)) {
                result.add(student);
            }
        }
        return result;
    }
}
  1. 开发过滤接口实现类:
package filter;

/** @author 周航宇 */
public class CriteriaOr implements Criteria {

    private final Criteria criteriaOne;
    private final Criteria criteriaTwo;

    public CriteriaOr(Criteria criteriaOne, Criteria criteriaTwo) {
        this.criteriaOne = criteriaOne;
        this.criteriaTwo = criteriaTwo;
    }

    @Override
    public Set<Student> filter(Set<Student> students) {
        Set<Student> result = new HashSet<>();
        // 过滤出满足条件1的集合结果1,Set自动去重
        Set<Student> resultOne = criteriaOne.filter(students);
        // 过滤出满足条件2的集合结果2,Set自动去重
        Set<Student> resultTwo = criteriaTwo.filter(students);
        // 求并集:将结果1和结果2都添加到result集合即可,Set自动去重
        result.addAll(resultOne);
        result.addAll(resultTwo);
        return result;
    }
}
  1. 开发过滤器接口实现类:
package filter;

/** @author 周航宇 */
public class CriteriaAnd implements Criteria {

    private final Criteria criteriaOne;
    private final Criteria criteriaTwo;

    public CriteriaAnd(Criteria criteriaOne, Criteria criteriaTwo) {
        this.criteriaOne = criteriaOne;
        this.criteriaTwo = criteriaTwo;
    }

    @Override
    public Set<Student> filter(Set<Student> students) {
        // 过滤出满足条件1的集合结果1,Set自动去重
        Set<Student> resultOne = criteriaOne.filter(students);
        // 过滤出满足条件2的集合结果2,Set自动去重
        Set<Student> resultTwo = criteriaTwo.filter(students);
        // 将满足条件1的集合结果1添加到result集合
        Set<Student> result = new HashSet<>(resultOne);
        // 求交集:将同时存在于result集合和resultTwo集合中的元素保留在result集合中
        result.retainAll(resultTwo);
        return result;
    }
}
  1. 开发测试类:
package filter;

/** @author 周航宇 */
public class FilterTest {
    @Test
    public void testFilter() {

        // 准备学生集合数据
        Set<Student> students = new HashSet<>();
        students.add(new Student("赵四", 58, 1));
        students.add(new Student("刘能", 59, 1));
        students.add(new Student("王云", 58, 0));
        students.add(new Student("刘英", 59, 0));

        // 使用过滤模式过滤学生
        System.out.println("全部女学生:" + new CriteriaGender(0).filter(students));
        System.out.println("全部男学生:" + new CriteriaGender(1).filter(students));
        System.out.println("全部58岁的学生:" + new CriteriaAge(58).filter(students));
        System.out.println("全部59岁的学生:" + new CriteriaAge(59).filter(students));
        System.out.println("全部女学生或58岁的学生:"
                + new CriteriaOr(new CriteriaGender(0), new CriteriaAge(58)).filter(students));
        System.out.println("全部女学生且58岁的学生:"
                + new CriteriaAnd(new CriteriaGender(0), new CriteriaAge(58)).filter(students));
    }
}

E04. 装饰器设计模式

心法:装饰器模式,全称 Decorator Pattern,用于在不改变现有对象结构的情况下,动态地给该对象拓展一些额外功能。

装饰器模式遵守开闭原则,对添加开放,对修改关闭。

使用子类继承同样可以达到同样的拓展效果,但设计模式不推荐使用继承。

武技:测试装饰器模式

  1. 开发英雄实体类:
package decorator.entity;

/** @author 周航宇 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hero {
    /** 英雄名称 */
    private String heroName;
    /** 攻击力 */
    private Integer ATT;
    /** 防御力 */
    private Integer DEF;
    /** 血量 */
    private Integer HP;
    /** 蓝量 */
    private Integer MP;

    /** 获取英雄信息 */
    public String getMessage() {
        return String.format("我是英雄%s:攻击%d,防御%d,血量%d,蓝量%d",
                heroName, ATT, DEF, HP, MP);
    }
}
  1. 开发装饰器接口:
package decorator;

/** @author 周航宇 */
public interface SkinDecorator {
    /** 获取英雄信息 */
    String getMessage();
}
  1. 开发装饰器实现类:
package decorator;

/** @author 周航宇 */
public class AlphaTeamSkinDecorator implements SkinDecorator {

    private final Hero hero;

    public AlphaTeamSkinDecorator(Hero hero) {
        this.hero = hero;
    }

    @Override
    public String getMessage() {
        // 原功能直接调用,并拓展新的功能
        return hero.getMessage() + ",使用 [阿尔法小队] 皮肤";
    }
}
  1. 开发装饰器实现类:
package decorator;

/** @author 周航宇 */
public class ElfKingSkinDecorator implements SkinDecorator {

    private final Hero hero;

    public ElfKingSkinDecorator(Hero hero) {
        this.hero = hero;
    }

    @Override
    public String getMessage() {
        // 原功能直接调用,并拓展新的功能
        return hero.getMessage() + ",使用 [精灵王] 皮肤";
    }
}
  1. 开发测试类:
package decorator;

/** @author 周航宇 */
public class DecoratorTest {
    @Test
    public void testDecorator() {
        // 创建后羿实体
        Hero hero = new Hero("后羿", 1, 2, 3, 4);
        // 创建后羿精灵王皮肤装饰器
        SkinDecorator skinDecorator = new ElfKingSkinDecorator(hero);
        // 获取英雄信息
        System.out.println("不通过装饰器:" + hero.getMessage());
        System.out.println("通过装饰器:" + skinDecorator.getMessage());
    }
}

E05. 组合设计模式

心法:组合设计模式,全称 Composite Pattern,也叫部分整体模式,使用树形结构来表示 部分 - 整体 的层次结构。

场景:组合设计模式的可扩展性高,适用于文件夹目录遍历,搭建下拉菜单等场景。

武技:测试组合设计模式

  1. 开发实体类:
package composite.entity;

/** @author 周航宇 */
@Data
public class Emp {
    private String empName;
    private Set<Emp> subEmps;

    public Emp(String empName) {
        this.empName = empName;
        subEmps = new HashSet<>();
    }

    /** 添加下属 */
    public void add(Emp subEmp) {
        this.subEmps.add(subEmp);
    }

    /** 删除下属 */
    public void del(Emp subEmp) {
        this.subEmps.remove(subEmp);
    }

    /** 查看下属 */
    public Set<Emp> list() {
        return subEmps;
    }
}
  1. 开发测试类:
package composite;

/** @author 周航宇 */
public class CompositeTest {
    @Test
    public void testComposite() {

        // 构建赵四家族企业树形结构
        Emp zhaosi = new Emp("赵四");
        Emp yutian = new Emp("赵玉田");
        Emp liuying = new Emp("刘英");
        liuying.add(new Emp("赵兰妮"));
        liuying.add(new Emp("赵兰娜"));
        zhaosi.add(yutian);
        zhaosi.add(liuying);

        // 构建谢广坤家族企业树形结构
        Emp guangkun = new Emp("谢广坤");
        Emp yongqiang = new Emp("谢永强");
        Emp xiaomeng = new Emp("王小蒙");
        xiaomeng.add(new Emp("谢成龙"));
        xiaomeng.add(new Emp("谢成凤"));
        yongqiang.add(new Emp("谢飞机"));
        guangkun.add(yongqiang);
        guangkun.add(xiaomeng);

        // 查看
        System.out.println(zhaosi.getEmpName() + "家族如下:");
        zhaosi.list().forEach(System.out::println);
        System.out.println(guangkun.getEmpName() + "家族如下:");
        guangkun.list().forEach(System.out::println);
    }
}

E06. 外观设计模式

心法:外观设计模式,全称 Facade Pattern,用于隐藏系统的复杂性,向客户端提供一个可以访问系统的接口,核心就是在客户端和复杂系统之间再加一层,该层提前将调用顺序和依赖关系等处理完毕。

优点:提高系统灵活性和安全性,减少系统相互之间的依赖。

缺点:不符合开闭原则,若需要进行修改,则非常麻烦。

武技:测试外观设计模式

  1. 开发图形接口类:
package facade;

/** @author 周航宇 */
public interface Shape {
    /** 绘制图形 */
    void draw();
}
  1. 开发图形接口实现类:
package facade;

/** @author 周航宇 */
public class CircleShape implements Shape {

    @Override
    public void draw() {
        System.out.println("模拟绘制一个圆圈");
    }
}
  1. 开发图形接口实现类:
package facade;

/** @author 周航宇 */
public class HeartShape implements Shape {
    @Override
    public void draw() {
        System.out.println("模拟绘制一个爱心");
    }
}
  1. 开发外观类:
package facade;

/** @author 周航宇 */
public class ShapeMaker {
    private final Shape circleShape;
    private final Shape heartShape;

    public ShapeMaker() {
        circleShape = new CircleShape();
        heartShape = new HeartShape();
    }

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

    public void drawHeart() {
        heartShape.draw();
    }
}
  1. 开发测试类:
package facade;

/** @author 周航宇 */
public class FacadeTest {
    @Test
    public void testFacade() {
        ShapeMaker shapeMaker = new ShapeMaker();
        shapeMaker.drawCircle();
        shapeMaker.drawHeart();
    }
}

E07. 享元设计模式

心法:享元设计模式,全称 Flyweight Pattern,主要用于减少重复对象的创建过程,减少内存占用,提高性能。

使用流程

  1. 创建一个工厂类,并在内部维护一个 HashMap<标识, 对象> 属性,用于缓存实例。
  2. 客户端通过工厂类来获取实例,并不使用 new 的方式进行实例创建。
  3. 若此时 HashMap 中已经缓存了相同的实例,则直接获取并返回,不用实例化。
  4. 若此时 HashMap 中并未缓存相同的实例,才进行实例化并返回。

武技:测试享元设计模式

  1. 开发实体类:
package flyweight.entity;

/** @author 周航宇 */
@Data
public class Car {
    /** 汽车品牌 */
    private String brand;

    public Car(String brand) {
        this.brand = brand;
    }
}
  1. 开发工厂类:这里虽然使用了 Factory,但它并非是工厂模式。
package flyweight;

/** @author 周航宇 */
public class CarFactory {

    /** 维护一个用于缓存Car实例的Map */
    private static final Map<String, Car> carMap = new HashMap<>();

    /** 根据品牌信息获取Car实例 */
    public static Car getCar(String brand) {
        // 尝试从carMap中获取指定品牌的汽车
        Car car = carMap.get(brand);
        // 若carMap中不存在该品牌的汽车,则创建实例并共享到carMap中
        if (car == null) {
            car = new Car(brand);
            carMap.put(brand, car);
            System.out.println(car + " 被实例化");
        }
        // 若carMap中存在该品牌的汽车,则直接返回
        return car;
    }
}
  1. 开发测试类:
package flyweight;

/** @author 周航宇 */
public class FlyWeightTest {
    @Test
    public void testFlyWeight() {
        // 重复创建5次宝马汽车,但实例化过程仅执行一次
        System.out.println(CarFactory.getCar("宝马"));
        System.out.println(CarFactory.getCar("宝马"));
        System.out.println(CarFactory.getCar("宝马"));
        System.out.println(CarFactory.getCar("宝马"));
        System.out.println(CarFactory.getCar("宝马"));
    }
}

E08. 代理设计模式

心法:JDK 动态代理要求代理的对象(客户)必须是某接口的实现类。

武技:测试 JDK 动态代理

  1. 开发客户接口:
package proxy;

/** @author 周航宇 */
public interface UserService {
    /** 客户的添加方法 */
    void create();

    /** 客户的查询方法 */
    void select();

    /** 客户的修改方法 */
    void update();

    /** 客户的删除方法 */
    void delete();
}
  1. 开发客户实现类:
package proxy;

/** @author 周航宇 */
public class UserServiceImpl implements UserService {
    @Override
    public void create() {
        System.out.println("执行添加方法");
    }

    @Override
    public void select() {
        System.out.println("执行查询方法");
    }

    @Override
    public void update() {
        System.out.println("执行修改方法");
    }

    @Override
    public void delete() {
        System.out.println("执行删除方法");
    }
}
  1. 开发JDK代理公司类:重写 InvocationHandler -> invoke() 工作清单方法:
package proxy;

/** @author 周航宇 */
public class JdkProxyCompany implements InvocationHandler {

    private Object customer;

    /**
     * 代理工作清单
     *
     * @param proxy  代理对象
     * @param method 代理方法的Method对象
     * @param args   代理方法的形参
     * @return 代理方法的返回值,若是void,就返回null
     */
    @SneakyThrows
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {

        System.out.println("调用鉴权方法..");

        // 反射方法:返回值 = 方法对象.invoke(客户对象, 方法形参)
        Object methodReturn = method.invoke(customer, args);

        System.out.println("记录日志..");

        return methodReturn;
    }

    /***
     * 聘用一个代理,聘用代理时必须告诉我客户是谁
     *
     * @param customer 客户实体
     * @return 代理对象
     */
    public Object hireProxy(Object customer) {
        this.customer = customer;

        // 创建代理时,需要获取客户的类加载器,客户的所有接口和任务清单所在类
        return Proxy.newProxyInstance(
                customer.getClass().getClassLoader(),
                customer.getClass().getInterfaces(), this);
    }
}
  1. 开发测试类:
package proxy;

/** @author 周航宇 */
public class JdkProxyTest {
    @Test
    public void testJdkProxy() {
        // 创建JDK代理公司实例
        JdkProxyCompany jdkProxyCompany = new JdkProxyCompany();
        // 创建客户类
        UserService userService = new UserServiceImpl();
        // 为指定客户类聘用代理:需要将代理强转为客户接口类型,否则无法调用业务方法
        UserService userServiceProxy = (UserService) jdkProxyCompany.hireProxy(userService);
        // 使用代理调用完成客户类的功能
        userServiceProxy.create();
        userServiceProxy.select();
        userServiceProxy.update();
        userServiceProxy.delete();
    }
}

Java道经第1卷 - 第8阶 - 设计模式(二)


传送门:JB1-8-设计模式(一)
传送门:JB1-8-设计模式(二)
传送门:JB1-8-设计模式(三)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值