在编程的世界里,掌握扎实的算法与数据结构功底固然重要,但构建灵活、可维护的软件系统,则更依赖于精妙的软件设计思想,即设计模式。为了帮助初学者更好地理解和记忆,我们不妨用一种有趣的视角——武侠江湖,来解读几种经典的设计模式。
单例模式:天下只此一人的掌门绝学
单例模式的核心是确保一个类只有一个实例,并提供一个全局访问点。这好比一个武林大派,天下只能有一位掌门,号令一出,莫敢不从。
其实现的关键在于将构造函数私有化,防止外部随意创建,并通过一个静态方法提供唯一的实例访问。
public class 明教教主 {
private static 明教教主 唯一教主;
private 明教教主() { // 构造函数私有,如同闭关,外人不得擅入
}
public static 明教教主 获取教主() {
if (唯一教主 == null) {
唯一教主 = new 明教教主(); // 开坛即位,创建唯一实例
}
return 唯一教主;
}
public void 号令天下() {
System.out.println("圣火令出,莫敢不从!");
}
}
但简单的实现(懒汉式)在多线程环境下可能产生多个“掌门”,造成混乱。因此,进阶的“双重检查锁定”版本应运而生,它在保证性能的同时确保了线程安全。
public static 明教教主 获取教主() {
if (唯一教主 == null) { // 第一重检查:快速探视
synchronized(明教教主.class) { // 加锁,重兵把守
if (唯一教主 == null) { // 第二重检查:再次确认
唯一教主 = new 明教教主();
}
}
}
return 唯一教主;
}
工厂模式:无招胜有招的剑法总纲
工厂模式的核心是定义一个创建对象的接口,但将具体创建哪个类实例的决定推迟到子类或一个独立的工厂类中。这如同令狐冲的独孤九剑,不拘泥于固定招式,针对不同敌人使出最合适的剑法。
首先,定义一个统一的“剑法”接口。
interface 剑法 {
void 出招();
}
class 华山剑法 implements 剑法 {
public void 出招() { System.out.println("苍松迎客!"); }
}
class 辟邪剑法 implements 剑法 {
public void 出招() { System.out.println("流星飞堕!"); }
}
然后,创建一个“剑法工厂”,由它来负责根据需求打造具体的宝剑(实例)。
class 剑法工厂 {
public static 剑法 打造宝剑(String 剑法名) {
switch(剑法名) {
case “华山“: return new 华山剑法();
case “辟邪“: return new 辟邪剑法();
default: throw new IllegalArgumentException(“无此剑法“);
}
}
}
使用者无需关心剑法是如何打造的,只需通过工厂获取并使用即可,实现了创建与使用的解耦。
剑法 我的剑 = 剑法工厂.打造宝剑(“华山“);
我的剑.出招(); // 输出:苍松迎客!
观察者模式:消息灵通的江湖情报网
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象(被观察者)状态改变时,所有依赖于它的对象(观察者)都会得到通知并自动更新。这就像丐帮的情报系统,总舵一有消息,所有分舵立刻知晓。
首先定义观察者接口。
interface 江湖消息监听 {
void 收到消息(String 消息);
}
实现具体的观察者,例如各个丐帮分舵。
class 丐帮分舵 implements 江湖消息监听 {
private String 分舵名;
public 丐帮分舵(String 分舵名) {
this.分舵名 = 分舵名;
}
@Override
public void 收到消息(String 消息) {
System.out.println(分舵名 + “收到消息:“ + 消息);
}
}
定义被观察者(消息中心),负责维护观察者列表并发送通知。
import java.util.ArrayList;
import java.util.List;
class 江湖消息中心 {
private List<江湖消息监听> 所有分舵 = new ArrayList<>();
public void 添加分舵(江湖消息监听 分舵) {
所有分舵.add(分舵);
}
public void 发布消息(String 消息) {
for(江湖消息监听 分舵 : 所有分舵) {
分舵.收到消息(消息);
}
}
}
适配器模式:化解冲突的乾坤大挪移
适配器模式将一个类的接口转换成客户期望的另一个接口,使原本因接口不兼容而无法一起工作的类可以协同工作。这正如张无忌的乾坤大挪移,能将异种真气化为己用。
假设我们现有的系统使用的是“九阳神功”接口。
interface 九阳神功 {
void 纯阳内力();
}
但新引入的一个模块(如西域武功)提供的是不兼容的“阴寒真气”。
class 西域内功 {
public void 阴寒真气() {
System.out.println(“寒冰绵掌!“);
}
}
此时,创建一个适配器,让西域内功也能以九阳神功的方式运转。
class 乾坤大挪移适配器 implements 九阳神功 {
private 西域内功 异种真气;
public 乾坤大挪移适配器(西域内功 真气) {
this.异种真气 = 真气;
}
@Override
public void 纯阳内力() {
// 适配过程:调用原有功能,并进行转化或包装
异种真气.阴寒真气();
System.out.println(“经乾坤大挪移,转化为至阳内力!“);
}
}
策略模式:随心所欲的武功切换
策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以相互替换。它让算法的变化独立于使用它的客户。这好比一位武林高手,可以根据对手不同,随时切换华山剑法、太极拳或六脉神剑。
定义统一的策略接口。
interface 攻击策略 {
void 执行();
}
实现多种具体的策略(算法)。
class 华山剑法策略 implements 攻击策略 {
@Override
public void 执行() {
System.out.println(“发动「有凤来仪」!“);
}
}
class 太极拳策略 implements 攻击策略 {
@Override
public void 执行() {
System.out.println(“发动「如封似闭」!“);
}
}
定义上下文(Context),它持有一个策略对象,并能动态切换。
class 武林高手 {
private 攻击策略 当前策略;
public void 设置策略(攻击策略 策略) {
this.当前策略 = 策略;
}
public void 发起攻击() {
if (当前策略 != null) {
当前策略.执行();
}
}
}
使用时,高手可以自由切换对敌策略。
武林高手 高手 = new 武林高手();
高手.设置策略(new 华山剑法策略());
高手.发起攻击(); // 输出:发动「有凤来仪」!
高手.设置策略(new 太极拳策略());
高手.发起攻击(); // 输出:发动「如封似闭」!
总结
通过以上武侠比喻,我们生动地阐述了单例、工厂、观察者、适配器和策略这五种常见设计模式的核心思想。理解设计模式,关键在于领悟其解决特定问题的“心法”,而非死记硬背“招式”。在实际的Java项目开发或任何面向对象编程中,识别出代码中存在的“坏味道”,并适时地运用合适的设计模式进行重构,才能逐步提升软件设计的功力,构建出更加健壮、灵活的系统。