设计模式
设计模式就像是软件开发中的"最佳实践",它们解决了我们在做软件设计时遇到的常见问题 。通过一些日常生活的例子,我们可以更容易地理解这些模式的真正含义。接下来,我将通过一些生活化的例子,来讲解 7 种常见的设计模式。
一、单例模式(Singleton)------ "限量版商品"
想象一下有一个限量版的奢侈品,你只能买到一个 。无论你怎么去别的商店,这个奢侈品只有一个唯一的存在 ,如果你再去买,它总是那个已经售出的对象。
单例模式就是确保一个类只有一个实例,并且提供一个全局访问点。
目的:
确保一个类只有一个实例,并提供一个全局访问点来获取该实例。
概念:
单例模式确保一个类只有一个实例,且这个实例能够被全局访问。它通常用于管理资源的共享(如配置管理器、数据库连接池等),避免重复创建实例和浪费资源。
代码实现:
你在整个系统中只需要一个对象,例如配置文件读取器、线程池、日志记录器等,避免重复创建相同的对象,节省内存和资源。
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
二、原型模式(Prototype)------ "借用模板"
如果你去做一个个性化的手工艺术品,你可以选择复制一个已有的设计模板,然后根据模板来制作更多相似的艺术品,而不是从头开始。这个模板就是原型,它让你通过复制现有对象来快速创建新对象。
目的:
通过复制现有对象来创建新对象,避免使用构造函数重新创建对象。
概念:
原型模式允许通过"克隆"一个对象来创建一个新对象,而不是使用 new 操作符创建。它用于对象的创建过程很复杂或资源开销较大的情况,通过复制现有对象提高创建效率。
代码实现:
当一个对象的创建代价很高,且有多个类似对象时,你可以通过复制已有对象(深拷贝或者浅拷贝)来创建新的对象,节省时间和资源。
java
public class User implements Cloneable {
private String name;
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}
三、工厂方法模式(Factory Method)------ "定制化生产"
如果你去买车,你会先选择车的类型(轿车、SUV、跑车等),然后厂家根据你的需求定制生产出来。你不用自己去制作车,而是通过工厂来提供不同类型的车。
目的:
定义一个创建对象的接口,让子类决定实例化哪个类。
概念:
工厂方法模式提供一个接口,用于创建对象,但将具体的对象创建推迟到子类中。它允许子类决定实例化哪个具体的类,通常用于需要创建多种类型的对象但不希望在代码中硬编码具体类名的场景。
代码实现:
如果你需要创建很多类型相似的对象(例如:不同风格的UI组件),通过工厂方法模式可以将对象的创建交给专门的"工厂"类,让你根据不同的需求选择合适的工厂生产相应的对象。
java
public interface Product {
void use();
}
public class ProductA implements Product {
public void use() {
System.out.println("Use A");
}
}
public interface Factory {
Product createProduct();
}
public class FactoryA implements Factory {
public Product createProduct() {
return new ProductA();
}
}
四、抽象工厂模式(Abstract Factory)------ "整套定制"
你去买一套厨房家具,家具店不仅仅卖餐桌,而是卖整套厨房家具(餐桌、椅子、碗柜等)。而且你可以选择不同风格(现代风、复古风、简约风等)。这就像是一个**"产品族"**,你不只买一个单品,而是一整套。
目的:
提供一个接口,用于创建一系列相关或相互依赖的对象,而不需要指定具体类。
概念:
抽象工厂模式定义了一个用于创建一组相关产品(如按钮、文本框等)的方法。它使得客户端可以通过工厂接口来创建产品,而不需要关心具体产品类的实现。这种模式通常用于涉及多个产品族(如不同操作系统的 UI 组件)的情况。
代码实现:
当你需要创建一组相关的对象时,抽象工厂模式可以通过不同的工厂来为你提供相关产品,而这些产品会共同属于一个类别(比如:Windows 风格的UI组件、Mac 风格的UI组件)。
java
interface Button { void paint(); }
interface TextBox { void draw(); }
interface UIAbstractFactory {
Button createButton();
TextBox createTextBox();
}
class WindowsFactory implements UIAbstractFactory {
public Button createButton() { return new WinButton(); }
public TextBox createTextBox() { return new WinTextBox(); }
}
class WinButton implements Button {
public void paint() {
System.out.println("Drawing Windows Button");
}
}
class WinTextBox implements TextBox {
public void draw() {
System.out.println("Drawing Windows TextBox");
}
}
五、建造者模式(Builder)------ "定制化汉堡"
你去汉堡店,店员问你:要加什锦蔬菜吗?加奶酪吗?加辣酱吗?你可以根据自己的口味定制一个汉堡,而不是吃标准的"固定菜单"汉堡。这就像建造者模式,让你通过不同的组合创建一个符合需求的复杂对象。
目的:
将复杂对象的构建过程与表示分离,使得同样的构建过程可以创建不同的表示。
概念:
建造者模式通过分步骤构建复杂对象。每个步骤的执行由一个独立的构建器类负责,最终由 director 类将这些构建步骤组合成一个完整的对象。它适用于创建复杂对象的场景,避免构造函数中的参数爆炸问题。
代码实现:
当你需要创建一个复杂对象,并且该对象的属性有很多时,建造者模式可以帮助你一步一步地设置这些属性,最后生成一个完整的对象。
java
public class User {
private String name;
private int age;
public static class Builder {
private String name;
private int age;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public User build() {
User user = new User();
user.name = this.name;
user.age = this.age;
return user;
}
}
}
//使用时调用下面的代码设置属性
Builder builder = new User.Builder();
builder.name("Tom").age(25);
六、解释器模式(Interpreter)------ "英语词典"
你需要翻译一篇文章,你会查字典来解释每个单词的意思。字典里每个词都对应一个"解释",最终你把这些词的解释拼凑起来,就能理解整篇文章。
目的:
给定一个语言,定义它的文法,并实现一个解释器来解释语言中的句子。
概念:
解释器模式用于实现一个解释器,能够根据一个定义好的语言文法规则来解释输入的句子。它通过定义文法规则的类来实现具体的解释,并且通常适用于构建语言、计算表达式、规则引擎等场景。
代码实现:
如果你需要实现一个表达式解析器或规则引擎时,解释器模式能够让你为每个语法规则编写一个解释器对象,然后通过它们来解析复杂的表达式。
java
interface Expression {
int interpret();
}
class NumberExpression implements Expression {
private int number;
public int interpret() {
return number;
}
}
七、命令模式(Command)------ "遥控器"
你家里有一个遥控器,它通过按下不同的按钮来控制不同的设备(电视、空调、灯光等)。每次按下按钮,遥控器都会发送一个命令给相应的设备,而你并不需要知道具体设备的操作细节。
目的:
将请求封装为对象,从而使你可以使用不同的请求、队列或日志请求,以及支持撤销操作。
概念:
命令模式将请求封装为一个对象,使得请求的发送者和接收者解耦。请求的发送者不需要知道具体的执行逻辑,它只需要向命令对象发送请求。这种模式常用于需要排队、日志、撤销操作、事务处理等场景。
代码实现:
当你希望解耦请求发起者和请求执行者时,命令模式可以把请求封装成对象,通过"命令"来统一管理和调用。
java
interface Command {
void execute();
}
class LightOnCommand implements Command {
private Light light;
public void execute() {
light.on();
}
}
class RemoteControl {
private Command command;
public void pressButton() {
command.execute();
}
}