设计模式 (2):8 种结构性模式
回顾上节:
- 随着对象种类、属性容量的扩大,创建具体对象、管理属性装配、快速复制等,都面临难题,这时产生了工厂、建造者、原型等设计模式;
- 单例模式也保护了全局变量,提高了全局访问、使用全局对象和接口的 安全性、规范性、可用性 等等
目录
1 适配器模式 (Adapter)
方法依赖别的接口,但不重载?
这种时候需要适配器模式
- 定义 适配器,聚合 被适配的接口,实现 需求的接口
类图和代码实现
类图:
代码实现:
class HuaweiComputer implements Computer{
String readSD(SDCard sd){
return sd.readSD();
} // Computer 接口这里就不写了
}
interface SDCard {
String readSD();
}
interface HardDisk {
String readHD();
}
class SD2HD_Adapter implements SDCard {
HardDisk hd;
@Override
String readSD(){
return hd.readHD();
}
}
2 装饰器模式 (Decorator)
动态添加 多层功能?用链表(对象存邻接点)
增强接口通过装饰器的递归调用实现,而整体的功能增强其实就是链表
类图和代码实现(本质 - 链表)
代码实现:
- 测试类:
Computer cpt = new HuaweiComputer();
cpt = new RTX4090(cpt);
cpt = new Memory16G(cpt);
cpt = new Memory16G(cpt);
cpt = new Disk256G(cpt);
System.out.println("总共花费:" + cpt.cost() + "元");
/**
* 输出结果:
外加外存:256G || 799 元
外加内存条:16G || 899 元
外加内存条:16G || 899 元
外加显卡:RTX4090 || 7999 元
Huawei基础电脑 || 8999 元
总共花费:19595元
*/
- 类与接口:
class HuaweiComputer implements Computer{
int cost(){
System.out.println("Huawei基础电脑 || 8999 元");
return 8999;
}
}
class RTX4090 extends ComputerDecorator {
Computer cpt;
RTX4090(Computer cpt){
this.cpt = cpt;
}
int cost(){
System.out.println("外加显卡:RTX4090 || 7999 元");
return 7999 + cpt.cost();
}
}
class Memory16G extends ComputerDecorator {
Computer cpt;
Memory16G(Computer cpt){
this.cpt = cpt;
}
int cost(){
System.out.println("外加内存条:16G || 899 元");
return 899 + cpt.cost();
}
}
class Disk256G extends ComputerDecorator {
Computer cpt;
Disk256G(Computer cpt){
this.cpt = cpt;
}
int cost(){
System.out.println("外加外存:256G || 799 元");
return 799 + cpt.cost();
}
}
思考:不用装饰器模式,如何实现?
不用装饰者 (链表)?那就用数组!
类定义:
class HuaweiComputer implements Computer{
int cost(){
System.out.println("Huawei基础电脑 || 8999 元");
return 8999;
}
}
class RTX4090 implements Computer {
int cost(){
System.out.println("外加显卡:RTX4090 || 7999 元");
return 7999;
}
}
class Memory16G implements Computer {
int cost(){
System.out.println("外加内存条:16G || 899 元");
return 899;
}
}
class Disk256G implements Computer {
int cost(){
System.out.println("外加外存:256G || 799 元");
return 799;
}
}
测试代码:
List<Computer> cpt = new ArrayList<>();
cpt.add(new HuaweiComputer());
cpt.add(new RTX4090());
cpt.add(new Memory16G());
cpt.add(new Memory16G());
cpt.add(new Disk256G());
int cost = 0;
for(Computer c : cpt)
cost += c.cost();
System.out.println("总共花费:" + cost + "元");
可以看到,要想实现和装饰着一样的功能,要额外写好多代码,而且还不便于理解这些代码逻辑
3 组合模式 (Composite)
同类对象组成 树或图(对象存邻接列表)
对于不同层级的集合,不定义每一层级的类,而是取它们的共性类作为节点类,建立一颗树,避免了继承和实现,减少了类爆炸
类图和代码实现
类图:略,主要是多叉树的结构
// 员工类
@AllArgumentsConstructor
class Emp {
String name; // 姓名
String position; // 职位
int salary; // 薪水
ArrayList<Emp> directSublist;// 直接下级
// 获得员工信息
public String getCorpInfo() {
return String.format(
"姓名: %s 职位: %s 薪水: %d 元",
name, position, salary
);
}
// 添加直属下级
public void addDirectSubEmps(Emp... emps) {
directSublist.addAll(Arrays.asList(emps));
}
// 返回直属下级列表
public List<AbsEmp> getDirectSubEmps() {
return directSublist;
}
}
4 桥接模式 (Bridge)
多个维度的全排列?应把维度抽离成属性
假如 Computer 的产品本身是一个维度,但是它还有诸如 Brand 维度,比如 Apple、Huawei 等,以及内存维度 Memory ,比如 16G、32G 等
如果定义成具体类,就会有:
HuaweiM16GComputer、AppleM16GComputer、HuaweiM32GComputer
等等很多类;
所以要把产品处理产品本质之外的维度抽离出来:
Computer { Brand, Memory }
这样就只需要定义一个类
类图和代码实现
类图,回头补充
代码实现:
- 测试类:
// 装配 华为电脑
Computer cpt = new HuaweiComputer(new MEM16G(),
new HD256G(), new RTX4090());
System.out.println("Huawei 电脑总共花费:" + cpt.cost() + "元");
System.out.println("===================");
// 装配 Mac 电脑
cpt = new MacComputer();
System.out.println("Mac 电脑总共花费:" + cpt.cost() + "元");
/**
* 测试结果
Huawei基础价格:7999 元
16G内存:899 元
256G硬盘:899 元
RTX4090显卡:8999 元
Huawei 电脑总共花费:18796元
===================
Mac基础价格:6999 元
Mac 电脑总共花费:6999元
*/
- 类与接口:
@Data
@AllArgsConstructor
@NoArgsConstructor
class HuaweiComputer implements Computer {
Memory mem;
HardDisk hd;
GPU gpu;
@Override
public int cost() {
System.out.println("Huawei基础价格:7999 元");
int res = 0;
if(mem != null) // 内存维度
res = mem.cost();
if(hd != null) // 硬盘维度
res += hd.cost();
if(gpu != null) // GPU 维度
res += gpu.cost();
return 7999 + res; // 总价
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class MacComputer implements Computer {
Memory mem;
HardDisk hd;
GPU gpu;
@Override
public int cost() {
System.out.println("Mac基础价格:6999 元");
int res = 0;
if(mem != null) // 内存维度
res = mem.cost();
if(hd != null) // 硬盘维度
res += hd.cost();
if(gpu != null) // GPU 维度
res += gpu.cost();
return 6999 + res; // 总价
}
}
class MEM16G implements Memory {
@Override
public int cost() {
System.out.println("16G内存:899 元");
return 899;
}
}
class HD256G implements HardDisk {
@Override
public int cost() {
System.out.println("256G硬盘:899 元");
return 899;
}
}
class RTX4090 implements GPU {
@Override
public int cost() {
System.out.println("RTX4090显卡:8999 元");
return 8999;
}
}
与装饰器模式、组合模式、抽象工厂模式
这些模式很相似,下面我列举不同点:
- 装饰器模式是存储同接口的邻接点,构造链表的模式;
- 组合模式是存储同接口的邻接列表,构造树 / 图的模式
- 桥接模式是存储一组不同接口,构造多个维度属性
- 抽象工厂是生产一组不同接口,创建多个接口的产品族工厂
5 外观模式 (Facade)
启动元神,只是按任意键这么简单?
一个复杂系统,他有很多个子系统构成,如果外部去控制子系统协作实现复杂系统的功能,过于复杂;
这时需要一个开始按钮,提供一个封装内部复杂,而调用简单的接口
类图和代码实现
暂略,参考 Web 开发的 Controller - Service - Dao - Domain 四层开发模式:
迪米特法则:使用者仅需要最少知识
在外观模式中,操作一个复杂系统,只需要简单的调用外观接口即可,这体现了迪米特法则
6 代理模式 (Proxy)
- AOP:在原方法的前置、后置或环绕位置上做增强
7 享元模式 (Flyweight)
对象的内部状态和外部状态
对象状态,表示对象表示、传达的信息
- 内部状态 是对象有限、固定的信息
- 外部状态 是对象变化、可变的信息
如果每次对象传达的信息,都要复制一份内部状态,太浪费时间,
频繁创建对象,且内部状态比重大
这个时候,创建对象需要将外部状态和内部状态分开记录,并用id做对应:
类图和代码实现
类图:
测试类:
/**
测试结果:
pool.Computer(
buyerName=罗一,
sharedInfo=pool.Shared(
brand=Huawei,
cpu=i9, mem=32G,
gpu=4090, price=13999))
pool.Computer(
buyerName=门酱,
sharedInfo=pool.Shared(
brand=Mac,
cpu=i5, mem=16G,
gpu=2060, price=6999))
*/
Client client = new Client();
client.init();
// 购买一台电脑
System.out.println(
client.buy("罗一", 10002));
System.out.println(
client.buy("门酱", 20001));
代码实现:
class Client {
ComputerFactory factory;
Client(){
factory = new ComputerFactory();
}
void init(){
factory.addShared(10001,
new Shared("Huawei", "i7", "16G", "3050", 7999));
factory.addShared(10002,
new Shared("Huawei", "i9", "32G", "4090", 13999));
factory.addShared(20001,
new Shared("Mac", "i5", "16G", "2060", 6999));
factory.addShared(20002,
new Shared("Mac", "i9", "32G", "4090", 14999));
}
Computer buy(String name, int id){
return factory.createComputer(name, id);
}
}
class ComputerFactory {
Map<Integer, Shared> pool; // 共享资源
ComputerFactory(){
pool = new HashMap<>();
}
// 增加共享资源
void addShared(int id, Shared shared){
pool.put(id, shared);
}
Computer createComputer(String buyerName, int id){
return new Computer(buyerName, pool.get(id));
}
}
@AllArgsConstructor
@ToString
class Shared {
String brand; // 品牌
String cpu; // CPU
String mem; // 内存
String gpu; // GPU
int price; // 购买价格
}
@AllArgsConstructor
@ToString
class Computer {
String buyerName; // 购买者姓名
Shared sharedInfo; // 共享信息
}