返回顶部

设计模式 (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; // 共享信息
}
posted @ 2023-10-15 21:02  你好,一多  阅读(5)  评论(0编辑  收藏  举报