参考链接干货分享 | 《设计模式之美》学习笔记 - 知乎 (zhihu.com)
总体来说设计模式分为三大类:
创建型模式 ,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式 ,共7种:适配器模式、装饰器模式、代理模式、桥接模式、外观(门面)模式、组合模式、享元模式。
行为型模式 ,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、状态模式、命令模式、备忘录模式、访问者模式、中介者模式、解释器模式。
ps:标红为常用模式,其余不常用。常用的也就4+4+6=14种模式。
学习设计模式前了解一些设计原则和思想
设计原则和思想
当然,这些设计原则和哲学是软件设计中非常重要的概念。它们旨在提高代码的可维护性、可扩展性和可读性。以下是每个原则的详细解释:
1. SRP - Single Responsibility Principle (单一职责原则)
**定义**:一个类应该只有一个引起它变化的原因。
**解释**:每个类都应该只有一个职责,只有一个导致其变化的原因。如果一个类承担了多个职责,当需求变化时就会导致类的多处修改,难以维护。
**示例**:假设一个类既负责用户数据的保存又负责用户数据的显示,这违反了SRP。可以拆分成两个类,一个负责数据的保存,另一个负责显示。
2. OCP - Open/Closed Principle (开放封闭原则)
**定义**:软件实体(类、模块、函数等)应该是对扩展开放的,但对修改封闭的。
**解释**:应通过扩展系统的行为来实现变化,而不是通过修改已有的代码。这减少了修改现有代码带来的风险。
**示例**:通过使用抽象类和接口,可以在不修改现有类的情况下扩展新功能。
3. LSP - Liskov Substitution Principle (里氏替换原则)
**定义**:子类型必须能够替换掉它们的基类型。
**解释**:任何使用基类型的地方都应该可以透明地使用其子类型。如果子类不能替代父类,则可能会破坏程序的正确性。
**示例**:假设有一个基类`Shape`和其子类`Rectangle`和`Square`,子类的方法不应该违反基类的方法行为。
4. ISP - Interface Segregation Principle (接口隔离原则)
**定义**:客户端不应该被迫依赖于它们不使用的方法。
**解释**:应当为客户端提供专用接口,而不是使用一个包含所有方法的庞大接口。这样可以防止客户端依赖不需要的方法。
**示例**:将一个庞大的接口拆分成多个小接口,每个接口只包含一个客户端需要的方法。
5. DIP - Dependency Inversion Principle (依赖倒置原则)
**定义**:高层模块不应该依赖低层模块,二者都应该依赖于抽象。抽象不应该依赖细节,细节应该依赖抽象。
**解释**:通过依赖抽象(如接口或抽象类)而不是具体实现,可以减少代码的耦合性,使系统更加灵活和可维护。
**示例**:使用接口来定义行为,具体实现类依赖接口,而不是直接依赖具体类。
6. KISS - Keep It Simple, Stupid (保持简单,愚蠢)
**定义**:系统应该尽可能保持简单。
**解释**:简单的系统更容易理解、维护和扩展。复杂性应当尽量避免。
**示例**:避免过度设计,不引入不必要的复杂性。
7. DRY - Don't Repeat Yourself (不要重复自己)
**定义**:每一块知识在系统中应该有唯一的、明确的、权威的表示。
**解释**:避免代码重复,重复的代码会导致难以维护和更新。如果一个地方的逻辑改变了,需要修改所有相关的地方。
**示例**:使用函数、类和模块来封装重复的代码逻辑。
8. LOD - Law of Demeter (迪米特法则)
**定义**:一个对象应该对其他对象有最少的了解。
**解释**:一个对象不应该知道太多其他对象的内部细节。它只应该和直接相关的对象交流,减少类之间的耦合。
**示例**:在方法中避免调用链(如`a.getB().getC().doSomething()`),而是通过中介者或传递方法减少耦合。
9. YAGNI - You Aren't Gonna Need It (你不会需要它)
**定义**:不要实现当前不需要的功能。
**解释**:避免过早优化或加入不必要的功能,只实现当前需求的功能。这样可以减少代码的复杂性和维护成本。
**示例**:在项目初期,不要为未来可能的需求实现功能,只关注当前的需求。
总结
这些设计原则和哲学是为了帮助开发者编写高质量、易维护和可扩展的代码。理解并应用这些原则可以显著提高软件开发的效率和质量。
创建型设计模式
创建型模式:用于解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码
1 单例模式
单例模式用来创建全局唯一的对象,一个类只允许创建一个对象实例。有几种经典的实现方式,分别是饿汉式,懒汉式、双重检测、静态内部类、枚举。
单例模式是一种设计模式,确保一个类只有一个实例,并提供对该实例的全局访问点。在Java中,实现单例模式的几种经典方式有饿汉式、懒汉式、双重检测、静态内部类和枚举。以下是每种方式的实现示例:
1. 饿汉式(Eager Initialization)
饿汉式在类加载时就创建实例,线程安全但可能造成资源浪费。
java
public class SingletonEager {
private static final SingletonEager INSTANCE = new SingletonEager();
private SingletonEager() {
// 私有构造方法,防止外部实例化
}
public static SingletonEager getInstance() {
return INSTANCE;
}
}
2. 懒汉式(Lazy Initialization)
懒汉式在第一次需要实例时才创建,非线程安全。
java
public class SingletonLazy {
private static SingletonLazy instance;
private SingletonLazy() {
// 私有构造方法,防止外部实例化
}
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
3. 双重检测(Double-Checked Locking)
双重检测在保证线程安全的同时提高了性能。
java
public class SingletonDoubleChecked {
private static volatile SingletonDoubleChecked instance;
private SingletonDoubleChecked() {
// 私有构造方法,防止外部实例化
}
public static SingletonDoubleChecked getInstance() {
if (instance == null) {
// 这段代码用于在实例为空时,锁定当前类以确保只有一个线程能进入同步块创建实例。
// synchronized关键字确保了同步块内的代码在多线程环境下是安全的。
// 这个锁是为了防止多个线程同时进入创建实例的代码块。
synchronized (SingletonDoubleChecked.class) {
if (instance == null) {
instance = new SingletonDoubleChecked();
}
}
}
return instance;
}
}
4. 静态内部类(Static Inner Class)
静态内部类方式利用类加载机制保证线程安全,延迟加载且性能高。
java
public class SingletonStaticInnerClass {
private SingletonStaticInnerClass() {
// 私有构造方法,防止外部实例化
}
private static class Holder {
private static final SingletonStaticInnerClass INSTANCE = new SingletonStaticInnerClass();
}
public static SingletonStaticInnerClass getInstance() {
return Holder.INSTANCE;
}
}
5. 枚举(Enum)
枚举方式是实现单例模式最简洁和安全的方式,天生保证线程安全和防止反序列化创建新对象。
java
// 定义枚举类型
public enum SingletonEnum {
// 唯一实例
INSTANCE;
// 可以在这里添加其他方法
public void doSomething() {
System.out.println("Doing something...");
}
// 示例方法,可以保存一些状态
private int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
// 使用枚举单例
public class Main {
public static void main(String[] args) {
// 访问单例实例
SingletonEnum singleton = SingletonEnum.INSTANCE;
// 调用单例的方法
singleton.doSomething();
// 设置和获取值
singleton.setValue(42);
System.out.println("Singleton value: " + singleton.getValue());
}
}
这几种方式各有优缺点,选择适合自己项目需求的方式实现单例模式是关键。
2 工厂模式
工厂模式包括简单工厂、工厂方法、抽象工厂3种细分模式,抽象工厂模式不常用。工厂模式用来创建不同但相关类型的对象,由给定的参数决定来创建哪种类型的对象。
好的,下面是使用Java实现的简单工厂模式和工厂方法模式的示例。
1. 简单工厂模式
简单工厂模式通过一个工厂类,根据传入的参数决定创建哪种具体类型的对象。它将对象的创建逻辑集中在一个地方。
示例:
假设我们要创建不同类型的产品,例如`Car`和`Bike`。
java
// 产品接口
interface Vehicle {
void drive();
}
// 具体产品类:Car
class Car implements Vehicle {
@Override
public void drive() {
System.out.println("Driving a car.");
}
}
// 具体产品类:Bike
class Bike implements Vehicle {
@Override
public void drive() {
System.out.println("Riding a bike.");
}
}
// 简单工厂类
class VehicleFactory {
public static Vehicle createVehicle(String type) {
if (type.equalsIgnoreCase("Car")) {
return new Car();
} else if (type.equalsIgnoreCase("Bike")) {
return new Bike();
} else {
throw new IllegalArgumentException("Unknown vehicle type.");
}
}
}
// 使用简单工厂模式
public class SimpleFactoryDemo {
public static void main(String[] args) {
Vehicle car = VehicleFactory.createVehicle("Car");
car.drive();
Vehicle bike = VehicleFactory.createVehicle("Bike");
bike.drive();
}
}
2. 工厂方法模式
工厂方法模式通过定义一个创建对象的接口,将实际创建工作推迟到子类中。它让一个类的实例化延迟到其子类。
示例:
我们用相同的例子,但这次使用工厂方法模式。
java
// 产品接口
interface Vehicle {
void drive();
}
// 具体产品类:Car
class Car implements Vehicle {
@Override
public void drive() {
System.out.println("Driving a car.");
}
}
// 具体产品类:Bike
class Bike implements Vehicle {
@Override
public void drive() {
System.out.println("Riding a bike.");
}
}
// 抽象工厂接口
interface VehicleFactory {
Vehicle createVehicle();
}
// 具体工厂类:CarFactory
class CarFactory implements VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Car();
}
}
// 具体工厂类:BikeFactory
class BikeFactory implements VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Bike();
}
}
// 使用工厂方法模式
public class FactoryMethodDemo {
public static void main(String[] args) {
VehicleFactory carFactory = new CarFactory();
Vehicle car = carFactory.createVehicle();
car.drive();
VehicleFactory bikeFactory = new BikeFactory();
Vehicle bike = bikeFactory.createVehicle();
bike.drive();
}
}
解释
简单工厂模式
-
**工厂类** (`VehicleFactory`):包含一个静态方法,用于根据传入的参数创建不同类型的对象。
-
**优点**:使用简单,集中管理对象创建。
-
**缺点**:增加新的产品类型时需要修改工厂类代码,违反开闭原则。
工厂方法模式
-
**抽象工厂接口** (`VehicleFactory`):定义一个创建对象的方法。
-
**具体工厂类** (`CarFactory` 和 `BikeFactory`):实现创建对象的方法,每个具体工厂负责创建一种具体产品。
-
**优点**:符合开闭原则,可以通过增加新的具体工厂类来扩展系统而不修改现有代码。
-
**缺点**:增加代码复杂性,需要创建更多的类。
这两种模式都有各自的适用场景,根据实际需求选择合适的模式是关键。
当然可以!简单工厂模式和工厂方法模式各自有其适用的场景和优缺点。了解这些场景可以帮助我们在实际开发中做出更合适的选择。
简单工厂模式适用场景
- **对象创建逻辑简单且稳定**:
- 如果对象创建逻辑比较简单,不会经常变化,那么简单工厂模式是合适的选择。因为简单工厂模式将所有对象创建逻辑集中在一个地方,这样的集中管理非常方便。
- **客户端只需要知道工厂类**:
- 客户端不需要关心具体产品类的实现,只需传入参数给工厂类即可获得产品对象。这种场景下,简单工厂模式能够降低客户端的复杂度。
- **产品类型较少**:
- 当产品种类较少且变化不频繁时,简单工厂模式能够有效管理这些产品的创建。
示例
适用于一个简单的图形绘制工具,支持创建不同类型的形状(例如圆形、矩形)。
java
public class ShapeFactory {
public static Shape createShape(String type) {
if (type.equalsIgnoreCase("Circle")) {
return new Circle();
} else if (type.equalsIgnoreCase("Rectangle")) {
return new Rectangle();
} else {
throw new IllegalArgumentException("Unknown shape type.");
}
}
}
工厂方法模式适用场景
- **对象创建逻辑复杂 且多变**:
- 当对象的创建过程比较复杂,或者对象的创建依赖于具体的配置、参数或外部资源时,工厂方法模式可以将这些复杂的创建过程封装在具体工厂类中。
- **系统需要拓展新的产品类型**:
- 如果系统需要在未来拓展新的产品类型,而不希望修改已有代码,工厂方法模式是理想的选择。它遵循开闭原则,通过增加新的具体工厂类来创建新的产品类型。
- **需要提供一系列相关的产品**:
- 当需要创建一系列相关的产品对象,且这些产品有一个共同的接口或父类时,工厂方法模式可以提供一种灵活且可扩展的方式来创建这些产品。
示例
适用于一个文档处理系统,可以创建不同类型的文档(例如Word文档、PDF文档)。
java
// 抽象产品接口
interface Document {
void open();
void save();
}
// 具体产品类:WordDocument
class WordDocument implements Document {
@Override
public void open() {
System.out.println("Opening Word document.");
}
@Override
public void save() {
System.out.println("Saving Word document.");
}
}
// 具体产品类:PdfDocument
class PdfDocument implements Document {
@Override
public void open() {
System.out.println("Opening PDF document.");
}
@Override
public void save() {
System.out.println("Saving PDF document.");
}
}
// 抽象工厂接口
interface DocumentFactory {
Document createDocument();
}
// 具体工厂类:WordDocumentFactory
class WordDocumentFactory implements DocumentFactory {
@Override
public Document createDocument() {
return new WordDocument();
}
}
// 具体工厂类:PdfDocumentFactory
class PdfDocumentFactory implements DocumentFactory {
@Override
public Document createDocument() {
return new PdfDocument();
}
}
// 使用工厂方法模式
public class FactoryMethodDemo {
public static void main(String[] args) {
DocumentFactory wordFactory = new WordDocumentFactory();
Document wordDoc = wordFactory.createDocument();
wordDoc.open();
wordDoc.save();
DocumentFactory pdfFactory = new PdfDocumentFactory();
Document pdfDoc = pdfFactory.createDocument();
pdfDoc.open();
pdfDoc.save();
}
}
总结
-
**简单工厂模式**适用于产品种类少且创建逻辑简单、稳定的场景。它通过一个工厂类集中管理对象创建,简化客户端代码,但不利于扩展新产品。
-
**工厂方法模式**适用于产品种类多且创建逻辑复杂、多变的场景。它通过定义抽象工厂接口和具体工厂类,实现了创建逻辑的封装和扩展,符合开闭原则,利于系统的扩展和维护。
选择哪种模式取决于系统的复杂性和对扩展性的需求。
3.抽象工厂模式
抽象工厂模式是一种创建型设计模式,它提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们的具体类。抽象工厂模式使得客户端可以通过接口创建产品对象,从而实现产品族的创建和互换。
适用场景
-
**系统要独立于它的产品创建、组合和表示时**。
-
**系统要由多个产品系列中的一个来配置时**。
-
**当需要强调一组相关的产品对象的设计以便进行联合使用时**。
-
**系统提供一个产品类库,只有产品接口而不是实现时**。
抽象工厂模式的结构
-
**抽象产品(Abstract Product)**:定义了产品的接口。
-
**具体产品(Concrete Product)**:实现了抽象产品接口。
-
**抽象工厂(Abstract Factory)**:声明了一组创建抽象产品的方法。
-
**具体工厂(Concrete Factory)**:实现了创建具体产品对象的方法。
示例:GUI 工具包
假设我们有一个跨平台的 GUI 工具包,需要创建一系列的按钮和文本框,这些按钮和文本框在不同操作系统(如 Windows 和 MacOS)上有不同的实现。
抽象产品接口
java
// 抽象产品:按钮
interface Button {
void paint();
}
// 抽象产品:文本框
interface TextField {
void render();
}
具体产品类
java
// 具体产品:Windows 按钮
class WindowsButton implements Button {
@Override
public void paint() {
System.out.println("Painting a Windows button.");
}
}
// 具体产品:MacOS 按钮
class MacOSButton implements Button {
@Override
public void paint() {
System.out.println("Painting a MacOS button.");
}
}
// 具体产品:Windows 文本框
class WindowsTextField implements TextField {
@Override
public void render() {
System.out.println("Rendering a Windows text field.");
}
}
// 具体产品:MacOS 文本框
class MacOSTextField implements TextField {
@Override
public void render() {
System.out.println("Rendering a MacOS text field.");
}
}
抽象工厂接口
java
// 抽象工厂
interface GUIFactory {
Button createButton();
TextField createTextField();
}
具体工厂类
java
// 具体工厂:Windows 工厂
class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public TextField createTextField() {
return new WindowsTextField();
}
}
// 具体工厂:MacOS 工厂
class MacOSFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacOSButton();
}
@Override
public TextField createTextField() {
return new MacOSTextField();
}
}
客户端代码
客户端代码通过使用抽象工厂接口来创建产品,而无需直接依赖具体产品类。
java
public class AbstractFactoryDemo {
private static Application configureApplication() {
Application app;
GUIFactory factory;
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("win")) {
factory = new WindowsFactory();
} else if (osName.contains("mac")) {
factory = new MacOSFactory();
} else {
throw new UnsupportedOperationException("Unsupported OS: " + osName);
}
app = new Application(factory);
return app;
}
public static void main(String[] args) {
Application app = configureApplication();
app.paint();
}
}
// 客户端类:应用程序
class Application {
private Button button;
private TextField textField;
public Application(GUIFactory factory) {
button = factory.createButton();
textField = factory.createTextField();
}
public void paint() {
button.paint();
textField.render();
}
}
解释
-
**抽象产品接口**:`Button` 和 `TextField` 定义了产品的公共接口。
-
**具体产品类**:`WindowsButton`, `MacOSButton`, `WindowsTextField`, `MacOSTextField` 实现了抽象产品接口,提供具体产品的实现。
-
**抽象工厂接口**:`GUIFactory` 定义了一组创建抽象产品的方法。
-
**具体工厂类**:`WindowsFactory` 和 `MacOSFactory` 实现了抽象工厂接口,负责创建具体产品。
-
**客户端代码**:`Application` 类通过抽象工厂接口创建具体产品,并且在 `main` 方法中配置应用程序的工厂,使用工厂创建产品。
总结
抽象工厂模式通过定义创建一系列相关或相互依赖的对象的接口,能够有效地分离具体产品的创建逻辑,提供灵活的产品族创建方式,从而提高系统的可扩展性和可维护性。
总结
- 简单工厂模式:适用于对象创建逻辑简单且稳定,产品种类较少的场景。它通过一个工厂类集中管理对象创建,但不利于扩展新产品。
- 工厂方法模式:适用于对象创建逻辑复杂且多变,系统需要扩展新的产品类型时。它通过定义抽象工厂接口和具体工厂类,实现创建逻辑的封装和扩展,符合开闭原则。
- 抽象工厂模式 :适用于系统需要独立于产品创建、组合和表示,系统要由多个产品系列中的一个来配置,或者需要创建一系列相关的产品对象时。它通过定义创建一系列相关或相互依赖对象的接口,实现产品族的创建和互换。就像这种GUI库,不同类型平台下包含多个组件,就适合抽象工厂模式。
3 建造者模式
建造者模式 (Builder Pattern)
**定义**:
建造者模式是一种创建型设计模式,它用于将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示。
**优点**:
-
**将复杂对象的构建过程封装起来**:使客户端代码简化,不需要关心构建过程的细节。
-
**支持不同的表示**:可以通过不同的建造者来创建不同表示的对象。
-
**逐步构建对象**:允许通过分步骤构建复杂对象。
**适用场景**:
-
**需要生成的对象具有复杂的内部结构**:对象的构建步骤是稳定的,但各部分的细节可能经常变化。
-
**需要生成的对象有不同的表示**:对象的构建步骤相同,但对象的具体表示可能不同。
结构
-
**Product(产品类)**:要创建的复杂对象。
-
**Builder(抽象建造者)**:定义了创建Product的各个子部件的抽象接口。
-
**ConcreteBuilder(具体建造者)**:实现Builder接口,构建和装配各个部件。
-
**Director(指挥者)**:负责安排复杂对象的建造顺序。它知道如何构建复杂对象。
示例:建造一辆汽车#### 产品类(Car)
java
public class Car {
private String engine;
private String wheels;
private String body;
public void setEngine(String engine) {
this.engine = engine;
}
public void setWheels(String wheels) {
this.wheels = wheels;
}
public void setBody(String body) {
this.body = body;
}
@Override
public String toString() {
return "Car [engine=" + engine + ", wheels=" + wheels + ", body=" + body + "]";
}
}
抽象建造者(CarBuilder)
java
abstract class CarBuilder {
protected Car car;
public Car getCar() {
return car;
}
public void createNewCar() {
car = new Car();
}
public abstract void buildEngine();
public abstract void buildWheels();
public abstract void buildBody();
}
具体建造者(SportsCarBuilder 和 SUVCarBuilder)
java
class SportsCarBuilder extends CarBuilder {
@Override
public void buildEngine() {
car.setEngine("Sports Engine");
}
@Override
public void buildWheels() {
car.setWheels("Sports Wheels");
}
@Override
public void buildBody() {
car.setBody("Sports Body");
}
}
class SUVCarBuilder extends CarBuilder {
@Override
public void buildEngine() {
car.setEngine("SUV Engine");
}
@Override
public void buildWheels() {
car.setWheels("SUV Wheels");
}
@Override
public void buildBody() {
car.setBody("SUV Body");
}
}
指挥者(Director)
java
class Director {
private CarBuilder carBuilder;
public void setCarBuilder(CarBuilder carBuilder) {
this.carBuilder = carBuilder;
}
public Car getCar() {
return carBuilder.getCar();
}
public void constructCar() {
carBuilder.createNewCar();
carBuilder.buildEngine();
carBuilder.buildWheels();
carBuilder.buildBody();
}
}
客户端代码
java
public class BuilderPatternDemo {
public static void main(String[] args) {
Director director = new Director();
// 建造一辆运动型汽车
CarBuilder sportsCarBuilder = new SportsCarBuilder();
director.setCarBuilder(sportsCarBuilder);
director.constructCar();
Car sportsCar = director.getCar();
System.out.println("Car built:\n" + sportsCar);
// 建造一辆SUV汽车
CarBuilder suvCarBuilder = new SUVCarBuilder();
director.setCarBuilder(suvCarBuilder);
director.constructCar();
Car suvCar = director.getCar();
System.out.println("Car built:\n" + suvCar);
}
}
解释
-
**产品类(Car)**:包含汽车的组成部分以及设置这些部分的方法。
-
**抽象建造者(CarBuilder)**:定义了构建汽车的步骤。
-
**具体建造者(SportsCarBuilder 和 SUVCarBuilder)**:实现了构建汽车的具体步骤。
-
**指挥者(Director)**:负责组织建造步骤,确保按照正确的顺序构建产品。
-
**客户端代码**:通过指挥者来构建具体的产品。
总结
建造者模式非常适合用来构建复杂对象,尤其是在对象的构建过程涉及多个步骤并且这些步骤可以灵活地配置或组合时。通过使用建造者模式,可以将对象的构建过程与其表示分离,从而实现代码的可读性、可维护性和扩展性。
4 原型模式
不用过多了解,知道即可。
**定义**:
原型模式是一种创建型设计模式,它允许一个对象再创建另外一个可定制的对象,而无需知道如何创建的细节。原型模式通过克隆已有的实例来创建新的对象。
**适用场景**:
-
**类初始化消耗较多资源**:当类的初始化过程比较复杂或消耗大量资源时,通过克隆现有实例可以节省开销。
-
**大量相似对象的创建**:在需要创建大量相似对象时,通过克隆现有实例可以简化创建过程。
-
**对象的差异化定制**:在需要不同配置的对象时,可以通过克隆并修改部分属性来实现。
结构
-
**Prototype(抽象原型类)**:定义一个克隆自身的接口。
-
**ConcretePrototype(具体原型类)**:实现克隆自身的方法。
-
**Client(客户端类)**:通过调用原型对象的克隆方法来创建新的对象。
示例:图形的克隆#### 抽象原型类(Shape)
java
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType() {
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
具体原型类(Rectangle 和 Circle)
java
public class Rectangle extends Shape {
public Rectangle() {
type = "Rectangle";
}
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Circle extends Shape {
public Circle() {
type = "Circle";
}
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
原型管理器(ShapeCache)
java
import java.util.Hashtable;
public class ShapeCache {
private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(), circle);
Rectangle rectangle = new Rectangle();
rectangle.setId("2");
shapeMap.put(rectangle.getId(), rectangle);
}
}
客户端代码
java
public class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape1 = ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape1.getType());
clonedShape1.draw();
Shape clonedShape2 = ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
clonedShape2.draw();
}
}
代码解释
- **抽象原型类(Shape)**:
-
包含一个 `clone()` 方法,用于克隆对象。
-
包含一些属性和方法,这些属性和方法将被具体原型类继承和实现。
- **具体原型类(Rectangle 和 Circle)**:
-
实现抽象原型类的 `draw()` 方法,定义具体原型的行为。
-
通过构造函数设置对象的类型。
- **原型管理器(ShapeCache)**:
-
`loadCache()` 方法预加载一些对象实例,并存储到一个哈希表中。
-
`getShape(String shapeId)` 方法通过对象的 ID 从哈希表中获取对象,并返回其克隆。
- **客户端代码(PrototypePatternDemo)**:
- 加载缓存对象,通过克隆方法获取新对象并使用。
优点
-
**性能提高**:通过克隆已有实例来创建对象,而不是通过构造函数重新创建,减少了开销。
-
**隐藏创建逻辑**:客户端不需要知道具体对象的创建细节,只需使用原型对象的克隆方法。
缺点
-
**深拷贝和浅拷贝**:需要考虑对象的拷贝是浅拷贝还是深拷贝。
-
**复杂性增加**:克隆方法的实现可能会比较复杂,尤其是当对象有复杂的引用关系时。
总结
原型模式是一种强大的创建型模式,它通过克隆现有对象来创建新的对象,适用于需要大量相似对象的场景。通过使用原型模式,可以提高性能并隐藏对象的创建逻辑,使代码更加简洁和易于维护。