Java道经第1卷 - 第8阶 - 设计模式(二)
传送门:JB1-8-设计模式(一)
传送门:JB1-8-设计模式(二)
传送门:JB1-8-设计模式(三)
文章目录
S06. 结构型设计模式
E01. 适配器设计模式
心法:适配器模式,全称 Adaptor Pattern,也叫包装器模式,其核心是将一个接口转换为客户端所期待的接口,从而使两个接口不兼容的类可以在一起工作。
优点:增强程序的可扩展性,此模式下,用户可以随意扩展程序的功能,却不需要修改原来的接口。
缺点:当系统中大量使用适配器模式时,可能会造成结构混乱,此时更建议直接对系统进行重构。
场景:
- 当内存卡和笔记本接口不兼容时,使用读卡器就是一种适配器模式的解决方案。
- 驱动技术大多使用的也是适配器模式,比如 JDBC 技术,其底层使用的就是适配器模式。
武技:测试适配器模式
- 开发原接口类:
package adapter;
/** 原接口:接口方法内容设计不够优化 */
public interface UserService {
void c();
void r();
void u();
void d();
}
- 开发新接口类:
package adapter;
/** 新接口:与原接口不兼容 */
public interface UserServicePro {
void insert();
void select();
void update();
void delete();
}
- 实现新接口类:
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("升级版的删除方法");
}
}
- 开发适配器类:
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();
}
}
- 开发测试类:
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
个实现类即可。
此模式下,相当于将品牌的选择和配置的组合方式,交给调用方来选择。
武技:测试桥接模式
- 开发手机配置的接口:
package bridge;
/** 手机配置接口 */
public interface PhoneConfig {
/** 手机颜色 */
String getColor();
/** 手机运存 */
Integer getRam();
/** 手机存储 */
Integer getRom();
}
- 开发手机低配置实现类:
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;
}
}
- 开发手机中配置实现类:
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;
}
}
- 开发手机高配置实现类:
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;
}
}
- 开发手机抽象类:
package bridge;
/** 手机抽象类 */
public abstract class Phone {
/** 手机的各种配置被剥离出去,并在此进行引入 */
protected PhoneConfig phoneConfig;
/** 抽象类的构造器只能由子类来调用 */
public Phone(PhoneConfig phoneConfig) {
this.phoneConfig = phoneConfig;
}
/** 获取手机的完整信息:具体实现由子类来决定 */
public abstract String getInfo();
}
- 开发华为手机类:
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());
}
}
- 开发小米手机类:
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());
}
}
- 开发测试类:
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 拼装,就是使用的该模式。
武技:测试过滤器设计模式
- 开发过滤实体类:
package filter.entity;
/** @author 周航宇 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private Integer age;
private Integer gender;
}
- 开发过滤接口:
package filter;
/** @author 周航宇 */
public interface Criteria {
/**
* 过滤方法
* @param students 被过滤的集合
* @return 过滤后,满足条件的集合
*/
Set<Student> filter(Set<Student> students);
}
- 开发过滤接口实现类:
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;
}
}
- 开发过滤接口实现类:
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;
}
}
- 开发过滤接口实现类:
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;
}
}
- 开发过滤器接口实现类:
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;
}
}
- 开发测试类:
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,用于在不改变现有对象结构的情况下,动态地给该对象拓展一些额外功能。
装饰器模式遵守开闭原则,对添加开放,对修改关闭。
使用子类继承同样可以达到同样的拓展效果,但设计模式不推荐使用继承。
武技:测试装饰器模式
- 开发英雄实体类:
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);
}
}
- 开发装饰器接口:
package decorator;
/** @author 周航宇 */
public interface SkinDecorator {
/** 获取英雄信息 */
String getMessage();
}
- 开发装饰器实现类:
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() + ",使用 [阿尔法小队] 皮肤";
}
}
- 开发装饰器实现类:
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() + ",使用 [精灵王] 皮肤";
}
}
- 开发测试类:
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,也叫部分整体模式,使用树形结构来表示
部分 - 整体
的层次结构。
场景:组合设计模式的可扩展性高,适用于文件夹目录遍历,搭建下拉菜单等场景。
武技:测试组合设计模式
- 开发实体类:
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;
}
}
- 开发测试类:
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,用于隐藏系统的复杂性,向客户端提供一个可以访问系统的接口,核心就是在客户端和复杂系统之间再加一层,该层提前将调用顺序和依赖关系等处理完毕。
优点:提高系统灵活性和安全性,减少系统相互之间的依赖。
缺点:不符合开闭原则,若需要进行修改,则非常麻烦。
武技:测试外观设计模式
- 开发图形接口类:
package facade;
/** @author 周航宇 */
public interface Shape {
/** 绘制图形 */
void draw();
}
- 开发图形接口实现类:
package facade;
/** @author 周航宇 */
public class CircleShape implements Shape {
@Override
public void draw() {
System.out.println("模拟绘制一个圆圈");
}
}
- 开发图形接口实现类:
package facade;
/** @author 周航宇 */
public class HeartShape implements Shape {
@Override
public void draw() {
System.out.println("模拟绘制一个爱心");
}
}
- 开发外观类:
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();
}
}
- 开发测试类:
package facade;
/** @author 周航宇 */
public class FacadeTest {
@Test
public void testFacade() {
ShapeMaker shapeMaker = new ShapeMaker();
shapeMaker.drawCircle();
shapeMaker.drawHeart();
}
}
E07. 享元设计模式
心法:享元设计模式,全称 Flyweight Pattern,主要用于减少重复对象的创建过程,减少内存占用,提高性能。
使用流程:
- 创建一个工厂类,并在内部维护一个
HashMap<标识, 对象>
属性,用于缓存实例。 - 客户端通过工厂类来获取实例,并不使用 new 的方式进行实例创建。
- 若此时 HashMap 中已经缓存了相同的实例,则直接获取并返回,不用实例化。
- 若此时 HashMap 中并未缓存相同的实例,才进行实例化并返回。
武技:测试享元设计模式
- 开发实体类:
package flyweight.entity;
/** @author 周航宇 */
@Data
public class Car {
/** 汽车品牌 */
private String brand;
public Car(String brand) {
this.brand = brand;
}
}
- 开发工厂类:这里虽然使用了 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;
}
}
- 开发测试类:
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 动态代理
- 开发客户接口:
package proxy;
/** @author 周航宇 */
public interface UserService {
/** 客户的添加方法 */
void create();
/** 客户的查询方法 */
void select();
/** 客户的修改方法 */
void update();
/** 客户的删除方法 */
void delete();
}
- 开发客户实现类:
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("执行删除方法");
}
}
- 开发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);
}
}
- 开发测试类:
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-设计模式(三)