工厂模式
通过将对象的创建过程封装到一个工厂类中,使得客户端不需要直接使用 new
去创建对象,而是通过调用工厂方法来获取所需的对象。这样可以降低代码耦合度,并方便后续的扩展和维护。
示例代码
简单工厂模式(不配合策略模式):
java">// 手机顶层接口
public interface Phone {void makeCall(String number);
}// 华为手机实现
public class HuaweiPhone implements Phone {@Overridepublic void makeCall(String number) {System.out.println("Using HuaweiPhone to call: " + number);}
}// 苹果手机实现
public class ApplePhone implements Phone {@Overridepublic void makeCall(String number) {System.out.println("Using ApplePhone to call: " + number);}
}// 工厂类
public class PhoneFactory {public static Phone createPhone(String type) {switch (type) {case "Huawei":return new HuaweiPhone();case "Apple":return new ApplePhone();default:throw new IllegalArgumentException("Unknown phone type: " + type);}}
}// 测试类
public class Main {public static void main(String[] args) {Phone huawei = PhoneFactory.createPhone("Huawei");huawei.makeCall("123456789");Phone apple = PhoneFactory.createPhone("Apple");apple.makeCall("987654321");}
}
工厂模式 + 策略模式
目的:当需要扩展更多设备(如平板)时,可以抽象顶层接口,将工厂类的职责划分得更清晰。
java">// 顶层接口:设备
public interface Device {void start();
}// 手机实现
public class PhoneDevice implements Device {@Overridepublic void start() {System.out.println("Starting Phone...");}
}// 平板实现
public class TabletDevice implements Device {@Overridepublic void start() {System.out.println("Starting Tablet...");}
}// 策略模式:设备工厂接口
public interface DeviceFactory {Device createDevice();
}// 手机工厂实现
public class PhoneFactory implements DeviceFactory {@Overridepublic Device createDevice() {return new PhoneDevice();}
}// 平板工厂实现
public class TabletFactory implements DeviceFactory {@Overridepublic Device createDevice() {return new TabletDevice();}
}// 客户端
public class Main {public static void main(String[] args) {DeviceFactory phoneFactory = new PhoneFactory();Device phone = phoneFactory.createDevice();phone.start();DeviceFactory tabletFactory = new TabletFactory();Device tablet = tabletFactory.createDevice();tablet.start();}
}
配合 Map
的映射关系
当设备种类较多时,可以用 Map
管理工厂类,动态获取:
java">import java.util.HashMap;
import java.util.Map;public class DeviceFactoryRegistry {private static final Map<String, DeviceFactory> factoryMap = new HashMap<>();static {factoryMap.put("Phone", new PhoneFactory());factoryMap.put("Tablet", new TabletFactory());}public static Device getDevice(String type) {DeviceFactory factory = factoryMap.get(type);if (factory == null) {throw new IllegalArgumentException("Unknown device type: " + type);}return factory.createDevice();}
}// 测试类
public class Main {public static void main(String[] args) {Device phone = DeviceFactoryRegistry.getDevice("Phone");phone.start();Device tablet = DeviceFactoryRegistry.getDevice("Tablet");tablet.start();}
}
结论
通过引入工厂模式和策略模式的结合,可以让代码更加灵活、可扩展,特别适用于需要动态创建不同类型对象的场景。
装饰器模式
- 目的:在不修改原有类的情况下,动态地增强类的功能。
- 前提:被增强的类需要有一个顶层的抽象接口。
- 实现:通过实现相同接口的装饰器类,将被增强的类作为成员变量进行组合(而不是继承),然后对其方法进行增强或扩展。
示例代码
核心结构
- 顶层接口:定义核心功能。
- 具体实现类:实现核心功能。
- 装饰器类:实现顶层接口,并将被装饰类作为成员变量,通过组合的方式增强功能。
代码实现
java">// 顶层接口
public interface Component {void operation();
}// 具体实现类
public class ConcreteComponent implements Component {@Overridepublic void operation() {System.out.println("ConcreteComponent: 原本的功能");}
}// 装饰器基类
public abstract class Decorator implements Component {protected Component component;public Decorator(Component component) {this.component = component;}@Overridepublic void operation() {component.operation();}
}// 第一个具体装饰器
public class DecoratorA extends Decorator {public DecoratorA(Component component) {super(component);}@Overridepublic void operation() {super.operation();addFunctionalityA();}private void addFunctionalityA() {System.out.println("DecoratorA: 增强功能 A");}
}// 第二个具体装饰器
public class DecoratorB extends Decorator {public DecoratorB(Component component) {super(component);}@Overridepublic void operation() {super.operation();addFunctionalityB();}private void addFunctionalityB() {System.out.println("DecoratorB: 增强功能 B");}
}// 测试类
public class Main {public static void main(String[] args) {// 原始组件Component component = new ConcreteComponent();// 增强功能 AComponent decoratorA = new DecoratorA(component);decoratorA.operation();System.out.println("----------");// 增强功能 A + BComponent decoratorB = new DecoratorB(decoratorA);decoratorB.operation();}
}
输出结果
ConcreteComponent: 原本的功能
DecoratorA: 增强功能 A
----------
ConcreteComponent: 原本的功能
DecoratorA: 增强功能 A
DecoratorB: 增强功能 B
装饰器模式的优点
- 开闭原则:在不修改原类的情况下,对功能进行扩展。
- 灵活性:可以动态组合多个装饰器,叠加增强功能。
- 替代继承:相比于直接继承扩展类功能,装饰器更灵活,不会导致类爆炸问题。
装饰器模式的缺点
- 复杂性:对于过多的装饰器,代码结构可能变得复杂。
- 性能开销:每一层装饰器都会增加额外的调用栈。
适配器模式
- 目标:使得不兼容的接口可以协同工作,适配器模式让一个类的接口变得与客户端期望的接口兼容。
- 工作原理:适配器类通过组合的方式持有旧接口对象,并通过重写方法来实现对旧接口的适配,让它能够符合新的接口要求。
- 典型场景:当你有一个过时的接口或第三方库,无法直接使用,但是你又不想修改旧的代码,适配器模式就非常适合。
示例代码
假设我们有一个音乐播放器,旧版本只支持 MP3
格式,而新版本支持 MP4
格式。我们可以使用适配器模式来将旧播放器适配到新的播放器接口。
1. 定义接口
- 新播放器接口:定义新版本播放器支持的播放方法。
- 旧播放器接口:定义老版本播放器支持的播放方法。
java">// 新播放器接口
public interface NewPlayer {void playMP4(String fileName);
}// 旧播放器接口
public interface OldPlayer {void playMP3(String fileName);
}
2. 旧播放器实现
旧播放器只能播放 MP3 格式。
java">// 旧的播放器实现
public class OldMP3Player implements OldPlayer {@Overridepublic void playMP3(String fileName) {System.out.println("Playing MP3 file: " + fileName);}
}
3. 新播放器实现
新播放器支持 MP4 格式。
java">// 新的播放器实现
public class NewMP4Player implements NewPlayer {@Overridepublic void playMP4(String fileName) {System.out.println("Playing MP4 file: " + fileName);}
}
4. 适配器类
适配器将旧播放器的 playMP3
方法适配为新播放器的 playMP4
方法。
java">// 适配器类:将旧播放器适配为新播放器
public class AdapterPlayer implements NewPlayer {private OldPlayer oldPlayer;public AdapterPlayer(OldPlayer oldPlayer) {this.oldPlayer = oldPlayer;}@Overridepublic void playMP4(String fileName) {// 当遇到 MP3 格式时,调用旧播放器播放System.out.println("Adapting to MP4 format, using old player to play MP3...");oldPlayer.playMP3(fileName);}
}
5. 客户端使用
在客户端使用时,可以通过适配器类使得旧播放器也能兼容新播放器的接口。
java">public class Main {public static void main(String[] args) {OldPlayer oldPlayer = new OldMP3Player();NewPlayer adapterPlayer = new AdapterPlayer(oldPlayer);// 通过适配器调用新播放器的接口adapterPlayer.playMP4("song.mp3");}
}
输出结果
Adapting to MP4 format, using old player to play MP3...
Playing MP3 file: song.mp3
适配器模式的优点
- 兼容性:适配器模式能够使得旧系统与新系统之间保持兼容,避免修改旧代码。
- 灵活性:适配器类可以根据不同的需求来适配多个接口,增加了系统的灵活性。
- 单一职责:适配器专门用于接口的转换,符合单一职责原则。
适配器模式的缺点
- 增加了代码复杂度:每个适配器类都会增加代码量,可能会增加维护的复杂性。
- 可能需要大量的适配器:如果需要适配的接口很多,适配器类的数量可能会增加,导致类的管理变得更加复杂。
组合模式
- 统一接口:通过定义一个顶层接口,使得客户端可以通过相同的方式来处理单个对象和组合对象(容器对象)。
- 递归结构:对象可以包含子对象,这样形成一个树形结构,允许递归组合。
- 灵活性:通过组合树中的节点(例如,单个区域或区域列表),可以灵活地构建复杂结构。
组合模式的应用场景
- 组织结构:例如公司组织结构,其中有公司、部门、员工等组成部分。
- 文件系统:文件夹中包含文件,文件夹也可以包含文件夹,形成递归的树形结构。
- 图形绘制:多个图形可以组合成一个复合图形,例如矩形、圆形等基本图形可以组合成更复杂的图形。
示例代码
假设你要统计中国各个省市的人口数量,可以使用组合模式来表示不同层级的区域(省、市等)并进行统计。
1. 定义顶层接口
java">// 区域接口,所有的区域类(省、市等)都需要实现这个接口
public interface Region {int getPopulation(); // 获取该区域的人口数
}
2. 具体实现类(叶子节点)
每个区域的具体实现类,如省或者市,都实现 Region
接口,并计算其人口数。
java">// 省级区域,叶子节点
public class Province implements Region {private String name;private int population;public Province(String name, int population) {this.name = name;this.population = population;}@Overridepublic int getPopulation() {System.out.println("统计 " + name + " 的人口: " + population);return population;}
}// 市级区域,叶子节点
public class City implements Region {private String name;private int population;public City(String name, int population) {this.name = name;this.population = population;}@Overridepublic int getPopulation() {System.out.println("统计 " + name + " 的人口: " + population);return population;}
}
3. 组合类(容器节点)
组合类可以包含多个 Region
对象,无论是单独的 Province
还是包含多个市的 RegionGroup
。
java">// 区域组,组合节点
import java.util.List;public class RegionGroup implements Region {private String name;private List<Region> subRegions;public RegionGroup(String name, List<Region> subRegions) {this.name = name;this.subRegions = subRegions;}@Overridepublic int getPopulation() {int totalPopulation = 0;System.out.println("统计 " + name + " 的人口总数:");for (Region region : subRegions) {totalPopulation += region.getPopulation();}return totalPopulation;}
}
4. 客户端使用
通过组合 RegionGroup
和 Region
,可以灵活地统计不同区域的人口。
java">import java.util.Arrays;public class Main {public static void main(String[] args) {// 创建市级区域Region changsha = new City("长沙市", 1000);Region zhuzhou = new City("株洲市", 800);// 创建省级区域Region hunan = new Province("湖南省", 10000);// 创建包含多个市的区域组合Region cityGroup = new RegionGroup("湖南省", Arrays.asList(changsha, zhuzhou));// 再创建包含省和市的更大组合Region country = new RegionGroup("中国", Arrays.asList(hunan, cityGroup));// 获取中国的总人口int totalPopulation = country.getPopulation();System.out.println("中国总人口:" + totalPopulation);}
}
组合模式的优点
- 简化客户端代码:客户端可以通过统一的
Region
接口来处理单个对象和组合对象,简化了代码的复杂性。 - 灵活扩展:可以方便地添加新的
Region
类型(如城市、地区等),而不需要修改现有的代码。 - 支持递归结构:组合模式使得树形结构的递归实现变得容易,适合表示具有层次结构的对象。
组合模式的缺点
- 增加了系统复杂性:由于组合结构的递归特性,如果层次结构复杂,系统的复杂度会增加。
- 不适合所有场景:如果对象之间没有明显的层级结构,组合模式可能会使得设计变得不必要复杂。
代理模式
- 代理角色:代理类代表另一个对象,并控制对该对象的访问。客户端与代理类交互,而不直接与目标对象(真实对象)交互。
- 真实角色:目标对象,执行实际的业务逻辑。
- 代理类型:
- 静态代理:代理类在编译时就已经存在,代理类和目标类之间的关系是静态的。
- 动态代理:代理类在运行时动态生成,通常通过反射机制创建,目标类和代理类的关系是动态的。
代理模式的常见应用场景
- 延迟加载:代理可以在需要时再创建真实对象,避免不必要的资源消耗。
- 权限控制:代理可以控制对目标对象的访问,确保只有经过授权的操作才能执行。
- 日志记录、性能监控:代理可以在调用目标对象之前或之后添加日志记录、性能监控等功能。
- 远程代理:在分布式系统中,代理可以用来代表远程对象,隐藏远程调用的细节。
示例代码
下面是一个简单的静态代理模式的示例:
1. 定义接口
java">// 接口,真实角色和代理角色都实现这个接口
public interface Subject {void request();
}
2. 真实角色
java">// 真实角色
public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("RealSubject: 执行请求");}
}
3. 代理角色
java">// 代理角色
public class ProxySubject implements Subject {private RealSubject realSubject;@Overridepublic void request() {// 在代理角色中加入额外的操作,例如权限验证、日志记录等if (realSubject == null) {realSubject = new RealSubject();}System.out.println("ProxySubject: 代理前的操作");realSubject.request(); // 调用真实角色的方法System.out.println("ProxySubject: 代理后的操作");}
}
4. 客户端代码
java">public class Client {public static void main(String[] args) {Subject proxy = new ProxySubject(); // 创建代理对象proxy.request(); // 通过代理对象调用请求}
}
输出结果
ProxySubject: 代理前的操作
RealSubject: 执行请求
ProxySubject: 代理后的操作
代理模式的优点
- 控制访问:代理对象可以控制对真实对象的访问,增加安全性和权限控制。
- 增加功能:代理类可以在不修改真实对象的前提下,增加额外的功能(如日志记录、事务处理等)。
- 延迟初始化:通过代理模式,可以在实际需要的时候再创建真实对象,节省资源。
- 透明性:代理模式使得客户端代码与真实对象的交互保持一致,客户端不需要关心代理对象的存在。
代理模式的缺点
- 增加系统复杂度:代理模式需要创建额外的代理对象,可能会增加系统的复杂性。
- 性能开销:由于代理对象在方法调用前后可能添加了额外的处理逻辑,可能会带来性能开销。
责任链模式
- 链条结构:多个处理对象(处理者)形成一个链条,每个处理对象处理某个特定的请求或任务。
- 职责划分:每个处理对象只负责自己特定的任务,且每个对象都知道下一个处理对象是哪个。
- 请求传递:请求从链头开始,依次传递给链中的各个处理对象,每个对象可以选择处理请求或将请求传递给链中的下一个对象。
- 解耦:客户端无需知道请求将被哪个处理对象处理,只需要将请求发送到链的开始,其他的交给链中的处理对象。
责任链模式的应用场景
- 日志处理:日志记录时可以通过不同的处理器(如输出到文件、数据库、控制台等)依次传递。
- 权限验证:请求可以在多个权限验证处理器之间传递,直到找到合适的处理器进行处理。
- 事件处理:UI 事件的处理,事件可以通过不同的事件处理器(如按钮点击、输入框变化等)传递。
示例代码
以下是责任链模式的简单实现:
1. 定义处理者接口
java">// 处理者接口,每个处理者处理某个请求,或者将请求传递给下一个处理者
public interface Handler {void setNextHandler(Handler nextHandler); // 设置下一个处理者void handleRequest(String request); // 处理请求
}
2. 具体处理者实现
java">// 处理者 A
public class HandlerA implements Handler {private Handler nextHandler;@Overridepublic void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic void handleRequest(String request) {if (request.equals("TaskA")) {System.out.println("HandlerA is handling the request.");} else if (nextHandler != null) {nextHandler.handleRequest(request); // 传递给下一个处理者} else {System.out.println("No handler can handle the request.");}}
}// 处理者 B
public class HandlerB implements Handler {private Handler nextHandler;@Overridepublic void setNextHandler(Handler nextHandler) {this.nextHandler = nextHandler;}@Overridepublic void handleRequest(String request) {if (request.equals("TaskB")) {System.out.println("HandlerB is handling the request.");} else if (nextHandler != null) {nextHandler.handleRequest(request); // 传递给下一个处理者} else {System.out.println("No handler can handle the request.");}}
}
3. 客户端使用
java">public class Main {public static void main(String[] args) {// 创建处理者Handler handlerA = new HandlerA();Handler handlerB = new HandlerB();// 设置责任链handlerA.setNextHandler(handlerB);// 客户端请求System.out.println("Requesting TaskA:");handlerA.handleRequest("TaskA");System.out.println("\nRequesting TaskB:");handlerA.handleRequest("TaskB");System.out.println("\nRequesting TaskC:");handlerA.handleRequest("TaskC");}
}
输出结果
Requesting TaskA:
HandlerA is handling the request.Requesting TaskB:
HandlerB is handling the request.Requesting TaskC:
No handler can handle the request.
责任链模式的优点
- 解耦:客户端不需要知道具体的处理者,只需要发送请求,链条中的处理者会依次处理请求。这降低了客户端与具体处理者之间的耦合。
- 动态处理:可以动态地改变链条的处理顺序,增加或移除处理者非常方便。
- 扩展性:可以通过添加新的处理者来扩展功能,符合开闭原则。
责任链模式的缺点
- 性能问题:如果链条过长,或者每个处理者的处理逻辑较复杂,可能会影响性能,因为请求会经过多次处理。
- 调试困难:链条结构可能导致请求在链中传递,调试时难以跟踪请求的流向。
总结
责任链模式通过将多个处理对象连接成一个链条,让请求在链中传递,直到某个处理对象能够处理它为止。每个处理对象可以选择处理请求或将请求转发给下一个处理对象。这种模式不仅解耦了请求的发送者与处理者,还提供了灵活的扩展机制。
建造者模式
没错,建造者模式(Builder Pattern)确实和链式调用(Fluent Interface)有很大的相似之处。它的核心思想就是将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
通过链式调用的方式,你可以一步步地为对象设置所需的属性,而不必关心对象具体是如何被构建的。建造者模式的优势在于:
- 解耦构建过程和表示:建造者模式将对象的构建逻辑与业务逻辑分开,便于维护和扩展。
- 链式调用:使代码简洁、直观,调用顺序也更加灵活。
- 代码可读性强:通过链式调用可以清晰地了解一个对象的构建过程。
举个简单的例子:
java">// 假设我们构建一个 Computer 对象
public class Computer {private String CPU;private String RAM;private String storage;// 私有构造函数,避免直接创建对象private Computer(Builder builder) {this.CPU = builder.CPU;this.RAM = builder.RAM;this.storage = builder.storage;}public static class Builder {private String CPU;private String RAM;private String storage;public Builder setCPU(String CPU) {this.CPU = CPU;return this;}public Builder setRAM(String RAM) {this.RAM = RAM;return this;}public Builder setStorage(String storage) {this.storage = storage;return this;}public Computer build() {return new Computer(this);}}@Overridepublic String toString() {return "Computer [CPU=" + CPU + ", RAM=" + RAM + ", Storage=" + storage + "]";}
}// 使用示例
public class Main {public static void main(String[] args) {Computer computer = new Computer.Builder().setCPU("Intel i7").setRAM("16GB").setStorage("512GB SSD").build();System.out.println(computer);}
}
模版模式
模版模式就是把通用的方法全都定义在一个抽象的父类,子类去继承他只专注于实现其他功能就行了是吧,比如泡茶,泡牛奶,都有一个公共的过程就是烧水。
抽象父类
java">public abstract class Beverage {// 模版方法,定义流程public final void prepare() {boilWater();brew();pourInCup();addCondiments();}// 通用方法,父类实现private void boilWater() {System.out.println("烧水...");}private void pourInCup() {System.out.println("将饮品倒入杯子...");}// 抽象方法,子类具体实现protected abstract void brew();protected abstract void addCondiments();
}
泡茶子类
java">public class Tea extends Beverage {@Overrideprotected void brew() {System.out.println("泡茶叶...");}@Overrideprotected void addCondiments() {System.out.println("添加柠檬...");}
}
泡牛奶子类
java">public class Milk extends Beverage {@Overrideprotected void brew() {System.out.println("加热牛奶...");}@Overrideprotected void addCondiments() {System.out.println("添加糖...");}
}
使用模版方法
java">public class Main {public static void main(String[] args) {Beverage tea = new Tea();tea.prepare();System.out.println("------");Beverage milk = new Milk();milk.prepare();}
}
输出结果
烧水...
泡茶叶...
将饮品倒入杯子...
添加柠檬...
------
烧水...
加热牛奶...
将饮品倒入杯子...
添加糖...
优点:
- 代码复用:通用部分(如烧水、倒入杯子)只需实现一次。
- 扩展性好:添加新类型的饮品只需创建新的子类即可。
- 灵活性:可以灵活地定义和调整每一步的具体实现。
模版模式适合用于具有固定流程但实现细节可变的场景,非常实用!