设计模式- Java

embedded/2024/12/3 7:39:00/

工厂模式

通过将对象的创建过程封装到一个工厂类中,使得客户端不需要直接使用 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();}
}
结论

通过引入工厂模式和策略模式的结合,可以让代码更加灵活、可扩展,特别适用于需要动态创建不同类型对象的场景。

装饰器模式

  1. 目的:在不修改原有类的情况下,动态地增强类的功能。
  2. 前提:被增强的类需要有一个顶层的抽象接口。
  3. 实现:通过实现相同接口的装饰器类,将被增强的类作为成员变量进行组合(而不是继承),然后对其方法进行增强或扩展。
示例代码
核心结构
  1. 顶层接口:定义核心功能。
  2. 具体实现类:实现核心功能。
  3. 装饰器类:实现顶层接口,并将被装饰类作为成员变量,通过组合的方式增强功能。
代码实现
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

装饰器模式的优点
  1. 开闭原则:在不修改原类的情况下,对功能进行扩展。
  2. 灵活性:可以动态组合多个装饰器,叠加增强功能。
  3. 替代继承:相比于直接继承扩展类功能,装饰器更灵活,不会导致类爆炸问题。
装饰器模式的缺点
  1. 复杂性:对于过多的装饰器,代码结构可能变得复杂。
  2. 性能开销:每一层装饰器都会增加额外的调用栈。

适配器模式

  1. 目标:使得不兼容的接口可以协同工作,适配器模式让一个类的接口变得与客户端期望的接口兼容。
  2. 工作原理:适配器类通过组合的方式持有旧接口对象,并通过重写方法来实现对旧接口的适配,让它能够符合新的接口要求。
  3. 典型场景:当你有一个过时的接口或第三方库,无法直接使用,但是你又不想修改旧的代码,适配器模式就非常适合。
示例代码

假设我们有一个音乐播放器,旧版本只支持 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. 兼容性:适配器模式能够使得旧系统与新系统之间保持兼容,避免修改旧代码。
  2. 灵活性:适配器类可以根据不同的需求来适配多个接口,增加了系统的灵活性。
  3. 单一职责:适配器专门用于接口的转换,符合单一职责原则。
适配器模式的缺点
  1. 增加了代码复杂度:每个适配器类都会增加代码量,可能会增加维护的复杂性。
  2. 可能需要大量的适配器:如果需要适配的接口很多,适配器类的数量可能会增加,导致类的管理变得更加复杂。

组合模式

  1. 统一接口:通过定义一个顶层接口,使得客户端可以通过相同的方式来处理单个对象和组合对象(容器对象)。
  2. 递归结构:对象可以包含子对象,这样形成一个树形结构,允许递归组合。
  3. 灵活性:通过组合树中的节点(例如,单个区域或区域列表),可以灵活地构建复杂结构。
组合模式的应用场景
  • 组织结构:例如公司组织结构,其中有公司、部门、员工等组成部分。
  • 文件系统:文件夹中包含文件,文件夹也可以包含文件夹,形成递归的树形结构。
  • 图形绘制:多个图形可以组合成一个复合图形,例如矩形、圆形等基本图形可以组合成更复杂的图形。
示例代码

假设你要统计中国各个省市的人口数量,可以使用组合模式来表示不同层级的区域(省、市等)并进行统计。

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. 客户端使用

通过组合 RegionGroupRegion,可以灵活地统计不同区域的人口。

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);}
}
组合模式的优点
  1. 简化客户端代码:客户端可以通过统一的 Region 接口来处理单个对象和组合对象,简化了代码的复杂性。
  2. 灵活扩展:可以方便地添加新的 Region 类型(如城市、地区等),而不需要修改现有的代码。
  3. 支持递归结构:组合模式使得树形结构的递归实现变得容易,适合表示具有层次结构的对象。
组合模式的缺点
  1. 增加了系统复杂性:由于组合结构的递归特性,如果层次结构复杂,系统的复杂度会增加。
  2. 不适合所有场景:如果对象之间没有明显的层级结构,组合模式可能会使得设计变得不必要复杂。

代理模式

  1. 代理角色:代理类代表另一个对象,并控制对该对象的访问。客户端与代理类交互,而不直接与目标对象(真实对象)交互。
  2. 真实角色:目标对象,执行实际的业务逻辑。
  3. 代理类型:
    • 静态代理:代理类在编译时就已经存在,代理类和目标类之间的关系是静态的。
    • 动态代理:代理类在运行时动态生成,通常通过反射机制创建,目标类和代理类的关系是动态的。
代理模式的常见应用场景
  • 延迟加载:代理可以在需要时再创建真实对象,避免不必要的资源消耗。
  • 权限控制:代理可以控制对目标对象的访问,确保只有经过授权的操作才能执行。
  • 日志记录、性能监控:代理可以在调用目标对象之前或之后添加日志记录、性能监控等功能。
  • 远程代理:在分布式系统中,代理可以用来代表远程对象,隐藏远程调用的细节。
示例代码

下面是一个简单的静态代理模式的示例:

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: 代理后的操作
代理模式的优点
  1. 控制访问:代理对象可以控制对真实对象的访问,增加安全性和权限控制。
  2. 增加功能:代理类可以在不修改真实对象的前提下,增加额外的功能(如日志记录、事务处理等)。
  3. 延迟初始化:通过代理模式,可以在实际需要的时候再创建真实对象,节省资源。
  4. 透明性:代理模式使得客户端代码与真实对象的交互保持一致,客户端不需要关心代理对象的存在。
代理模式的缺点
  1. 增加系统复杂度:代理模式需要创建额外的代理对象,可能会增加系统的复杂性。
  2. 性能开销:由于代理对象在方法调用前后可能添加了额外的处理逻辑,可能会带来性能开销。

责任链模式

  1. 链条结构:多个处理对象(处理者)形成一个链条,每个处理对象处理某个特定的请求或任务。
  2. 职责划分:每个处理对象只负责自己特定的任务,且每个对象都知道下一个处理对象是哪个。
  3. 请求传递:请求从链头开始,依次传递给链中的各个处理对象,每个对象可以选择处理请求或将请求传递给链中的下一个对象。
  4. 解耦:客户端无需知道请求将被哪个处理对象处理,只需要将请求发送到链的开始,其他的交给链中的处理对象。
责任链模式的应用场景
  • 日志处理:日志记录时可以通过不同的处理器(如输出到文件、数据库、控制台等)依次传递。
  • 权限验证:请求可以在多个权限验证处理器之间传递,直到找到合适的处理器进行处理。
  • 事件处理: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.
责任链模式的优点
  1. 解耦:客户端不需要知道具体的处理者,只需要发送请求,链条中的处理者会依次处理请求。这降低了客户端与具体处理者之间的耦合。
  2. 动态处理:可以动态地改变链条的处理顺序,增加或移除处理者非常方便。
  3. 扩展性:可以通过添加新的处理者来扩展功能,符合开闭原则。
责任链模式的缺点
  1. 性能问题:如果链条过长,或者每个处理者的处理逻辑较复杂,可能会影响性能,因为请求会经过多次处理。
  2. 调试困难:链条结构可能导致请求在链中传递,调试时难以跟踪请求的流向。
总结

责任链模式通过将多个处理对象连接成一个链条,让请求在链中传递,直到某个处理对象能够处理它为止。每个处理对象可以选择处理请求或将请求转发给下一个处理对象。这种模式不仅解耦了请求的发送者与处理者,还提供了灵活的扩展机制。

建造者模式

没错,建造者模式(Builder Pattern)确实和链式调用(Fluent Interface)有很大的相似之处。它的核心思想就是将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

通过链式调用的方式,你可以一步步地为对象设置所需的属性,而不必关心对象具体是如何被构建的。建造者模式的优势在于:

  1. 解耦构建过程和表示:建造者模式将对象的构建逻辑与业务逻辑分开,便于维护和扩展。
  2. 链式调用:使代码简洁、直观,调用顺序也更加灵活。
  3. 代码可读性强:通过链式调用可以清晰地了解一个对象的构建过程。

举个简单的例子:

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();}
}
输出结果
烧水...
泡茶叶...
将饮品倒入杯子...
添加柠檬...
------
烧水...
加热牛奶...
将饮品倒入杯子...
添加糖...

优点:

  1. 代码复用:通用部分(如烧水、倒入杯子)只需实现一次。
  2. 扩展性好:添加新类型的饮品只需创建新的子类即可。
  3. 灵活性:可以灵活地定义和调整每一步的具体实现。

模版模式适合用于具有固定流程但实现细节可变的场景,非常实用!


http://www.ppmy.cn/embedded/142540.html

相关文章

三格电子—单通道串口服务器

型号&#xff1a;SG-TCP232-110 一、产品介绍 1.1 功能简介 SG-TCP232-110 是一款用来进行串口数据和网口数据转换的设备。解决普通 串口设备在 Internet 上的联网问题。 设备的串口部分提供一个 232 接口和一个 485 接口&#xff0c;两个接口内部连接&#xff0c;…

阿里邮箱发送带excel附件邮件

导包 <dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4.7</version> </dependency> 内容 调用 EmilUtil.sendEmail("xxxx163.com",host,username,password,port,excelFile,…

axios的认识与基本使用

axios简介 Axios 是一个基于 promise 网络请求库&#xff0c;作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。 主要特点 从浏览器创建 XML…

DOCKER学习总结

这里写目录标题 一、Docker安装1.1 在线安装1.2 离线安装安装配置启动服务 1.3 配置镜像1.4 Docker启动相关命令 二、Docker三大核心概念2.1 镜像2.2 容器2.3 仓库2.3.1 公有仓库2.3.2 私有仓库 二、容器与虚拟机比较 一、Docker安装 1.1 在线安装 查看是否安装dockeryum lis…

【Robocasa】Code Review

文章目录 OverviewalgoInitializationImportant Class MethodsTrain LoopTest Time ConfigsdemoConfig FactoryConfig StructureConfig Locking默认锁定状态配置修改的上下文管理器 dataset示例数据集对象参数说明 model基础模块EncoderCoreVisualCoreScanCore随机化器 (Random…

VINS_MONO视觉导航算法【二】论文讲解+GPU实现调研

文章目录 其他文章说明论文系统特点和创新点鲁棒的初始化紧耦合优化的视觉惯性里程计&#xff08;VIO&#xff09;回环检测与重定位4自由度姿态图优化 视觉和IMU测量的预处理步骤视觉测量预处理特征跟踪特征点处理关键帧选择 IMU测量预处理IMU噪声和偏差预积分偏差校正 单目紧密…

详解MyBatis之篇一

目录 MyBatis 定义 使用MyBatis操作数据库 创建项目 配置 演示 UserInfo.java UserInfoMapper UserInfoMapperTest 数据准备 自动生成测试类 运行结果 MyBatis 定义 MyBatis 是一个优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避…

基于群晖搭建个人图书架-TaleBook based on Docker

前言 在群晖Container Manager中部署失败&#xff0c;转通过ssh部署。 一、准备工作 名称备注群晖SSH“终端机和SNMP”中启用SSH软件secureCRT等docker-compose.ymlGithub下载并修改 二、过程 2.1 创建本地文件夹 本地路径为&#xff1a; /docker/Calibre/data 2.2 下载d…