设计模式之外观模式

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

解析这个定义:

  1. 统一的接口,用来访问子系统中的一群接口”,如果不这么做之前,当多个客户端时,每个客户端都需要实现调用一群接口的逻辑,实现重复且复杂。且客户端耦合每个子系统的具体实现,不符合开闭原则。用一个统一接口封装一群接口,客户端只需要依赖一个类的一个接口即可。
  2. “高层接口”,说明外观类是在子系统之上,这是一个架构结构上的变化。
💡
现代计算机的启动过程涉及多个硬件组件(如CPU、内存、硬盘等)的协同工作,每个组件都有自己的初始化逻辑。

没有外观模式时的实现:

// 子系统类(与之前相同)
class CPU {
    public void start() { System.out.println("CPU启动"); }
}

class Memory {
    public void load() { System.out.println("内存加载"); }
}

class HardDrive {
    public void read() { System.out.println("硬盘读取"); }
}

// 客户端代码(直接调用子系统)
public class ClientWithoutFacade {
    public static void main(String[] args) {
        // 客户端需要知道所有子系统组件
        CPU cpu = new CPU();
        Memory memory = new Memory();
        HardDrive hardDrive = new HardDrive();
        
        // 客户端需要了解正确的启动顺序
        cpu.start();
        memory.load();
        hardDrive.read();
        
        System.out.println("计算机启动完成");
    }
}

这个实现存在以下问题:

  1. 客户端复杂度高:客户端需要了解所有子系统组件及其交互方式。
  2. 紧耦合:客户端直接依赖具体子系统类,违反迪米特法则(最少知识原则)。
  3. 难以维护:如果启动顺序或组件变化,需要修改所有客户端代码。

当这些子系统的启动逻辑需要给许多个客户端使用时,很自然的会想到,将他们抽取到一个中间层去实现,客户端不需要知道这么多细节。

// 子系统类
class CPU {
    public void start() { System.out.println("CPU启动"); }
}

class Memory {
    public void load() { System.out.println("内存加载"); }
}

class HardDrive {
    public void read() { System.out.println("硬盘读取"); }
}

// 外观类
class ComputerFacade {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;
    
    public ComputerFacade() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
    }
    
    public void start() {
        cpu.start();
        memory.load();
        hardDrive.read();
        System.out.println("计算机启动完成");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        ComputerFacade computer = new ComputerFacade();
        computer.start(); // 只需调用一个简单方法
    }
}

咋一看,似乎并没有什么区别,在client中简化的操作,被移到了facade类中去了而已。关键在于这种移动带来的架构层面的优势,而不仅仅是代码位置的改变。

没有Facade时,责任分散在所有客户端中,每个使用子系统的客户端都必须了解,当启动顺序需要变成memory.load() → hardDrive.read() → cpu.start()时,必须修改所有客户端。

而使用Facade后,责任集中在Facade这一个类中,所有客户端只需要知computer.start(); ,隐藏具体细节,修改启动顺序时,只需修改ComputerFacade类。

外观模式比较简单,它的价值在于:集中控制点、知识封装、批量维护优势

Read more

痛风带来的思考

昨晚一罐冰啤酒下去,睡觉时就感觉脚踝隐隐发作,果然早上起床直接下不来地。跟崴脚的感觉十分相似,无法行走,只能坐在一起上滑动,公司上班也去不了了,呆呆得躺在家里,下午疼痛感加剧,整个心思都在左脚的疼痛上,没有其他任何多余的精力去关注其他事情,而此刻的阳台,乃最美人间四月天,春日的微风吹拂着阳台的花儿,温暖的阳光抛洒下来,一切都如此惬意,而我却无心欣赏。 人在健康时,生活中有好多问题,但人在不健康时,生活中只剩一个问题。 我对这句话的理解更深刻了。人是健忘的,在疫情期间、在手术期间,这种感悟其实已经很深刻了,但是病情好转之后,人还是会被日常的琐碎、工作的烦扰搅乱心绪,没有专注的去享受生活本身的美好。 幸福的秘密在于,去享受我们所拥有的,而不是顽固的去追求所没有拥有的。阳光、草木、微风,都是幸福的玩意儿,应尽情的享受。 再等两天,脚完全恢复好了,身体健健康康后,我要以更轻盈的姿态去生活,不纠结他人的看法,不执着别人的认可,关注自己的能力,享受拥有的生后。 还有一个反省,针对咖啡、酒、烟,

By 李浩

设计模式之命令模式

命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,命令模式也支持可撤销的操作。 来解析这个定义: 1. “将一个请求封装为一个对象”,请求原本是一个方法,现在要封装成一个对象,说明要新增类来完成。 2. “可以用不同的请求对客户进行参数化”,说明是将命令对象作为参数进行传递。 3. “队列”说明需要维护命令多个命令的列表队列。 4. “撤销”说明有命令对象有undo撤销方法。 命令模式在设计模式中,算是一个比较不好理解的模式,很重要的原因是不清楚设计意图,不清楚不用这个模式前有何问题,这个模式带了哪些好处,能解决什么问题。 上一篇状态模式中,看到了状态模式抽离的是状态(属性),向上提成状态对象。有了这个基础,再来理解命令模式就相对简单了。命令模式抽离的是行为(方法),向上提成命令对象。 两者都通过“对象化”来解耦和扩展系统,但解决的问题不同: * 状态模式:处理对象内部状态驱动的行为变化。 * 命令模式:处理行为请求的封装与调度。 💡智能家居遥控器,假设我们有一个智能家居遥控器,可以控制 灯(Light) 和

By 李浩

设计模式之状态模式

状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。 来解析这个定义: 1. “内部状态”表明对象内部有一个属性来表示状态。 2. “内部状态改变时改变它的行为,对象看起来好像修改了它的类”说明状态改变后对象的行为发生了非常大的变化,不像是同一类的行为。 从目前的分析中似乎无法推导出状态模式的类图结构。 从实际的例子出来,来看看状态模式是如何演进而来。 💡我们有一个文档审批系统,文档有以下状态和转换: 1. 草稿(Draft) → 提交 → 待审批(PendingReview) 2. 待审批 → 批准 → 已发布(Published) 3. 待审批 → 拒绝 → 草稿 4. 已发布 → 撤回 → 草稿 从直觉出发,会使用条件语句实现需求逻辑。 public class Document { private String state = "DRAFT"; // 初始状态为草稿 public void submit(

By 李浩

设计模式之代理模式

代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。 解析这个定义: 1. “替身”表明在客户端看来,代理类与被代理类是同一类别,对客户端来说看上去没什么区别,依然能够满足诉求。如此可以看出代理类与被代理类来自同一个超类。 2. “控制对这个对象的访问”,能够控制访问,说明前提是能够访问,才能在访问之前做这个限制,即代理持有对真实对象的引用(或能创建它)。 当然可能会质疑,用继承的方式不是也能完成目标吗,用代理类去继承被代理类,然后重写方法,加入控制逻辑。但这违背了"组合优于继承"原则,代理类与被代理类强耦合。 💡我们要开发一个图片查看器,需求如下: 1. 图片加载开销大(从磁盘或网络加载耗时),希望首次显示时才加载(延迟加载)。 2. 某些图片需要权限校验,只有授权用户才能查看。 3. 客户端代码应统一接口,无需关心是直接加载图片还是通过代理。 // 1. 抽象接口(Subject) interface Image { void display(); } // 2. 真实对象(RealSubject)

By 李浩