创建型模式
- 1.工厂方法模式(Factory Method)
- 1.1 Shape 接口
- 1.2 实现 Shape 接口的类
- 1.3 ShapeFactory 类
- 1.4 使用 ShapeFactory 来获取 Shape 对象
- 1.5 优势
- 1.6 为什么感觉创建对象逻辑更复杂了?
- 1.7 结论
- 2.抽象工厂模式(Abstract Factory)
- 2.1 产品接口-首先是 Color 和 Shape 的接口
- 2.2 具体产品类-接下来是 Color 和 Shape 接口的具体实现。
- 2.3 抽象工厂接口-定义一个创建产品对象的工厂接口
- 2.4 具体工厂类 - 为每个产品家族创建一个具体工厂类。
- 2.5 客户端代码-使用工厂类来获取对象
- 2.6 抽象工厂模式是对工厂方法模式的进一步封装吗?
- 3.单例模式(Singleton Pattern)
- 3.1 示例
- 3.2 说明
- 4. 建造者模式(Builder Pattern)
- 4.1 构建 Car 类
- 4.2 定义 Builder 接口
- 4.3 实现 Builder 接口
- 4.4 使用建造者模式
- 5. 原型模式(Prototype Pattern)
创建型模式一共有5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式。
1.工厂方法模式(Factory Method)
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。
以下是一个简单的java示例,演示了工厂方法模式。我们将创建一个 Shape 接口和两个实现了该接口的的类 Circle 和 Rectangle 。此外,我们还将创建一个 ShapeFactory 类,该类使用工厂方法来生成基于给定信息的 Shape 对象。
1.1 Shape 接口
首先,我们定义一个 Shape 接口,它将被不同的形状类实现。
public interface Shape {// 定义一个画图的抽象方法void draw();
}
1.2 实现 Shape 接口的类
然后,我们创建两个实现了 Shape 接口的类:Circle 和 Rectangle。
public class Circle implements Shape {@Overridepublic void draw() {System.out.println("Inside Circle::draw() method.");}
}
public class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("Inside Rectangle::draw() method.");}
}
1.3 ShapeFactory 类
接下来,我们创建一个 ShapeFactory 类,它有一个工厂方法 getShape,该方法根据传入的信息返回对应的 Shape 对象。注意,这里的工厂方法是抽象的,意味着具体的工厂类(即 ShapeFactory 的子类)将负责实现它。
然而,为了简单起见,我们可以直接在 ShapeFactory 中实现这个方法,并通过一个参数(如形状类型)来决定创建哪种形状。这不是最纯粹的工厂方法模式实现,但足以展示概念。
public class ShapeFactory {// 使用 getShape 方法获取形状类型的对象public Shape getShape(String shapeType){if(shapeType == null){return null;}if(shapeType.equalsIgnoreCase("CIRCLE")){return new Circle();} else if(shapeType.equalsIgnoreCase("RECTANGLE")){return new Rectangle();}return null}
}
1.4 使用 ShapeFactory 来获取 Shape 对象
最后,我们使用 ShapeFactory 来获取 Shape 对象,并调用其 draw 方法。
public class FactoryPatternDemo {public static void main(String[] args) {ShapeFactory shapeFactory = new ShapeFactory();// 获取 Circle 的对象,并调用它的 draw 方法Shape Shape1 = shapeFactory.getShape("CIRCLE");shape1.draw();// 获取 Rectangle 的对象,并调用它的 draw 方法Shape shape2 = shapeFactory.getShape("Rectangle");shape2.draw();}}
在这个例子中,虽然我们没有直接展示工厂方法模式的子类化实现(即没有创建 ShapeFactory 的子类来覆盖 getShape 方法),但我们已经展示了如何使用工厂方法来根据输入参数动态地创建不同类型的对象。这是工厂方法模式的一个基本实现方式。
1.5 优势
- 解耦:工厂方法模式通过将对象的创建逻辑封装在工厂类中,实现了客户端代码与具体产品类之间的解耦。这样,当需要引入新的产品类时,只需新增一个具体的工厂类,而无需修改客户端代码。
- 可扩展性:由于客户端代码是通过接口或抽象类与工厂类交互的,因此可以轻松添加新的产品类和对应的工厂类,而无需修改现有代码。这种可扩展性对于大型项目或需要频繁变更的系统尤为重要。
- 封装性:工厂方法模式将对象的创建逻辑封装在工厂类中,客户端代码无需知道对象是如何创建的,只需通过工厂类获取所需的对象即可,这种封装性有助于隐藏系统的复杂性,使系统更加易于理解和维护。
- 遵循开闭原则:工厂方法模式遵循开闭原则(Open-Closed principle),即对扩展开放、对修改关闭。这意味着系统可以很容易地扩展新的功能(通过添加新的工厂类和产品类),而无需修改现有的代码。
1.6 为什么感觉创建对象逻辑更复杂了?
如果您感觉使用工厂方法模式后创建对象的逻辑变得更复杂了,这可能是因为:
- 初期学习成本:学习新的设计模式需要一定的时间和努力,特别是当您习惯了直接通过 new 关键字创建对象时。
- 过度设计:在某些情况下,如果系统本身并不复杂,或者对扩展性的需求不高,那么使用工厂方法模式可能会显得过于复杂。过度设计会增加系统的复杂性和维护成本。
- 实现不当:如果工厂方法模式的实现方式不当,比如将过多的逻辑放在工厂类中,欧哲工厂类的设计不够清晰和简洁,那么确实会导致代码变得复杂和难以理解。
1.7 结论
工厂方法模式是一种强大的设计模式,它提供了高度的灵活性和扩展性,有助于构建易于维护和扩展的系统。然而,是否使用这种模式取决于具体的应用场景和需求。在决定使用之前,应该仔细权衡其优缺点,并确保实现方式得当。
2.抽象工厂模式(Abstract Factory)
它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。在抽象工厂模式中,客户端不依赖于产品类实例如何被创建、组合和表达的细节,这对于产品的家族提供了高度的接口。
以下是一个使用Java语言实现的抽象工厂模式的示例。在这个例子中,我们将创建两个产品家族:Color(颜色)和 Shape(形状),每个家族都有两个具体的实现。
2.1 产品接口-首先是 Color 和 Shape 的接口
// Color.java
public interface Color {void fill();
}
// Shape.java
public interface Shape {void draw();
}
2.2 具体产品类-接下来是 Color 和 Shape 接口的具体实现。
// Red.java
public class Red implements Color {@Overridepublic void fill() {System.out.println("Inside Red::fill() method.");}
}
// Blue.java
public class Blue implements Color {@Overridepublic void fill() {System.out.println("Inside Blue::fill() method.");}
}
// Circle.java
public class Circle implements Shape {@Overridepublic void draw() {System.out.println("Inside Circle::draw() method");}
}
// Rectangle.java
public class Rectangle implements Shape {@Overridepublic void draw(){System.out.println("Inside Rectangle::draw() method.");}
}
2.3 抽象工厂接口-定义一个创建产品对象的工厂接口
// AbstractFactory.java
public interface AbstractFactory {Color getColor(String color);Shape getShape(String shape);
}
2.4 具体工厂类 - 为每个产品家族创建一个具体工厂类。
// ShapeFactory.java
public class ShapeFactory extends AbstractFactory { @Override public Shape getShape(String shapeType) { if (shapeType == null) { return null; } if (shapeType.equalsIgnoreCase("CIRCLE")) { return new Circle(); } else if (shapeType.equalsIgnoreCase("RECTANGLE")) { return new Rectangle(); } return null; } @Override public Color getColor(String color) { // 这里返回null,因为ShapeFactory专注于生产形状 return null; }
}
// ColorFactory.java
public class ColorFactory extends AbstractFactory { @Override public Color getColor(String color) { if (color == null) { return null; } if (color.equalsIgnoreCase("RED")) { return new Red(); } else if (color.equalsIgnoreCase("BLUE")) { return new Blue(); } return null; } @Override public Shape getShape(String shapeType) { // 这里返回null,因为ColorFactory专注于生产颜色 return null; }
}
2.5 客户端代码-使用工厂类来获取对象
// FactoryPatternDemo.java
public class FactoryPatternDemo {public static void main() {// 获取形状工厂AbstractFactory shapeFactory = new ShapeFactory();// 获取形状对象,并调用它的draw方法Shape shape1 = shapeFactory.getShape("CIRCLE");shape1.draw();Shape shape2 = shapeFactory.getShape("RECTANGLE");shape2.draw();// 获取颜色工厂AbstractFactory colorFactory = new ColorFactory();// 获取颜色工厂AbstractFactory colorFactory = new ColorFactory();// 获取颜色对象,并调用它的fill方法Color color1 = colorFactory.getColor("RED");color1.fill();Color color2 = colorFactory.getColor("BLUE");color2.fill();}
}
2.6 抽象工厂模式是对工厂方法模式的进一步封装吗?
是的,你说得对。抽象工厂模式(Abstract Factory Pattern)可以被视为对工厂方法模式(Factory Method Pattern)的一种扩展或进一步封装,特别是在处理具有多个产品族(即多个相关或依赖的产品接口的实现)的情况下。
在工厂方法模式中,我们主要关注于定义一个用于创建对象的接口,但让子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。然而,当需要创建的对象是多个产品族中的一部分时,每个产品族都包含多个产品时,工厂方法模式可能变得复杂,因为你需要为每个产品族中的每个产品都定义一个工厂方法。
这时,抽象工厂模式就提供了一个更好的解决方案。它定义了一个接口用于创建相关或依赖对象的家族,而无需指定它们具体的类。每个具体工厂类负责创建一个产品族中的对象。这样,客户端就不需要知道具体产品的类名,只需要通过抽象工厂接口就可以获取一组相关的产品对象。
简而言之,抽象工厂模式封装了多个具有共同主体的产品族的创建,而工厂方法模式则主要关注于单个产品的创建。抽象工厂模式提供了更高的抽象层次和更大的灵活性,特别是当需要处理多个产品族时。然而,这也意味着抽象工厂模式可能会比工厂方法模式更加复杂和难以维护,因此使用时需要权衡其优缺点。
3.单例模式(Singleton Pattern)
单例模式(Singleton Pattern)是一种常用的软件设计模式,用于确保一个类仅有一个实例,并提供一个全局访问点来获取该实例。在java中,实现单例模式有几种常见的方法,包括懒汉式(线程不安全)、懒汉式(线程安全)、饿汉式、双重检查锁定(Double-Checked Locking)以及使用枚举等。
下面是一个使用饿汉式实现的单例模式示例,这种方法基于类加载机制避免了多线程同步问题,但是实例在类装载时就完成实例化,没有达到Lazy Loading的效果。
3.1 示例
public class Singleton {// 1. 将构造方法私有化,防止外部通过new创建实例private Singleton() {}// 2. 在内部创建一个该类的实例,使用private static修饰,防止外部直接访问private static Singleton instance = new Singleton();// 3. 提供一个公共的静态方法,返回该实例对象public static Singleton getInstance() {return instance;}// 提供一个示例方法,用于演示单例模式的使用public void doSomthing() {System.out.println("Doing something...");}// 测试单例模式public static void main() {// 获取实例Singleton singleton1 = Singleton.getInstance();Singleton singleton2 = Singleton.getInstance();// 比较两个实例是否为同一个对象System.out.println(singleton1 == singleton2);// 输出 true// 调用示例方法singleton1.doSomething();singleton2.doSomething();}}
3.2 说明
在这个例子中,Singleton 类通过一个私有的静态变量 instance 来持有类的唯一实例,并通过私有的构造方法防止外部通过 new 关键字创建类的实例。同事,Singleton 类提供了一个公共的静态方法 getInstanc(),用于返回类唯一的实例。由于类的静态变量在类加载时初始化,并且 JVM 保证类的加载是线程安全的,因此这个实现是线程安全的。
然而,需要注意的是,饿汉式单例模式在类加载时就完成了实例的初始化,如果实例的初始化过程比较复杂或者耗时较长,那么这种方式可能会导致程序启动较慢。相比之下,懒汉式单例模式在需要时才进行实例的初始化,可以实现更高效的资源利用,但需要注意线程安全问题。
4. 建造者模式(Builder Pattern)
建造者模式(Builder Pattern)是一种创建型设计模式,允许通过链式方法逐步构建一个复杂对象。一个“Builder”类与产品的构造代码分离,使得同样的构建过程可以创建不同的表示。建造者模式主要用于创建一些复杂的对象,这些对象内部构建间的依赖关系较多,或者对象的不同表示或者配置都需要通过构建得到。
下面是一个Java语言实现建造者模式的简单示例。假设我们要创建一个 car(汽车)对象,Car 对象有多个属性,如品牌、型号颜色等。
4.1 构建 Car 类
这是我们要构建的产品类。
public class Car { private String brand; private String model; private String color; // 私有构造函数,防止外部直接实例化 private Car() {} // 必要的getter和setter public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public String getModel() { return model; } public void setModel(String model) { this.model = model; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } // 可以添加一个toString方法,方便查看构建结果 @Override public String toString() { return "Car{" + "brand='" + brand + '\'' + ", model='" + model + '\'' + ", color='" + color + '\'' + '}'; }
}
4.2 定义 Builder 接口
这是一个构建 Car 对象的接口,定义构建过程。
public interface CarBuilder {CarBuilder setBrand(String brand);CarBuilder setModel(String model);CarBuilder setColor(String color);Car builder();
}
4.3 实现 Builder 接口
实现具体的构建逻辑
public class CarBuilderImpl implements CarBulder {private Car car = new Car();@Overridepublic CarBuilder setBrand(String brand) {car.setBrand(brand);return this;}@Overridepublic CarBuilder setModel(String model) {car.setModel(model);return this;}@Overridepublic CarBuilder setColor(String color) {car.setColor(color);return this;}@Overridepublic Car build() {return car;}}
4.4 使用建造者模式
public class BuilderPatternDemo {public static void main(String[] args) {// 创建建造者CarBuilder carBuilder = new CarBuilderImpl();// 通过建造者构件对象Car myCar = carBuilder.setBrand("Toyota").setModel("Corolla").setColor("red").build();// 使用构建好的对象System.out.println(myCar);}}
以上代码展示了如何使用建造者模式来构建 Car 对象。通过 CarBuilderImpl 类,我们可以链式地设置 Car 的各个属性,并最终通过 build() 方法返回构建好的 Car 对象。这种方式使得 Car 类的构建过程更加灵活,切代码的可读性和可维护性也更高。
5. 原型模式(Prototype Pattern)
原型模式(Prototype Pattern)是一种创建型设计模式,用于通过复制一个已存在的实例来创建新的实例。原型模式允许一个对象创建一个自己的拷贝,而不需要使创建对象的类显式地指定它要被拷贝的构造函数或方法。
在Java中,原型模式的一个关键点是使用 Object 类的 clone() 方法。但是,要注意,clone() 方法是受保护的,因此,要在自己的类中实现克隆功能,通常需要重写 clone() 方法,并声明为 public。此外,自己的类还需要实现 Cloneable 接口,否则在调用 clone() 方法时会抛出 CloneNotSupportedException 异常。
下面是一个使用 Java 实现原型模式的简单例子:
// 实现Cloneable接口以允许克隆
public class Sheep implements Cloneable { private String name; // 构造函数 public Sheep(String name) { this.name = name; } // Getter和Setter public String getName() { return name; } public void setName(String name) { this.name = name; } // 重写clone()方法 @Override protected Sheep clone() throws CloneNotSupportedException { // 调用Object的clone()方法创建对象的拷贝 return (Sheep) super.clone(); } // 可以添加一个方法,对外提供安全的克隆调用 public Sheep deepClone() throws CloneNotSupportedException { return (Sheep) this.clone(); } // 示例:toString()方法,方便打印Sheep对象信息 @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + '}'; } // 测试类 public static void main(String[] args) { try { Sheep original = new Sheep("Dolly"); Sheep cloned = original.deepClone(); System.out.println("Original Sheep: " + original); System.out.println("Cloned Sheep: " + cloned); // 验证两个对象是否相等(物理地址不同,但内容相同) System.out.println("Original == Cloned? " + (original == cloned)); // 应为false System.out.println("Original.equals(Cloned)? " + original.equals(cloned)); // 默认的equals比较的是对象引用,这里也会是false,但你可以重写equals来比较内容 } catch (CloneNotSupportedException e) { e.printStackTrace(); } }
}