1. 定义
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的方式。 工厂模式(Factory Pattern)的核心思想是将对象的实例化过程和使用过程分离,客户端只需要工厂接口获取所需要的对象,而无需关心对象的创建细节。
分类:
工厂模式(Factory Pattern)共有三种:
- 简单工厂模式(Simple Factory Pattern)
- 工厂方法模式(Factory Method Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
优缺点:
优点:
- 解耦: 工厂模式将对象的创建和使用过程分离,使得客户端在无需关心对象的创建过程,只需要通过工厂获取所需对象即可,这种解耦使得代码更加的清晰, 易于理解和维护。
- 减少代码重复: 当需要创建的对象构造过程复杂或者需要在多个地方创建相同类型的对象时,使用工厂模式可以避免重复编写相同的代码。工厂负责统一管理和创建对象,减少了代码的冗余和重复。
- 灵活性: 工厂模式允许在运行时灵活的创建所需对象,这提供了更大的灵活性。例如:当需要根据不同的条件或者配置创建不同对象时,工厂模式可以轻松应对这种变化。
- 可维护性: 由于工厂模式将对象的创建过程封装在工厂类中,因此需要修改对象的创建过程,只需找到对应的工厂类修改其逻辑即可,而无需修改客户端的相关代码。这样极大的提高了代码的可维护性,降低了维护成本,并且使得代码更加健壮和更易于扩展。
缺点:
- 增加系统复杂度: 随着产品的增加,相应的工厂类也需要增加,这会导致系统中的类数量增多,从而在一定程度上增加了系统的复杂度。
- 破坏封装性: 某些情况下,工厂模式会破坏代码的封装性。例如:在简单工厂模式中:工厂类通常需要根据传入的参数来判断并创建对应的对象,这可能会导致工厂类和多个产品类之间产生紧密的耦合,违法了面向对象设计的单一职责原则和开放封闭原则。
- 不易扩展:当需要添加新产品时,可能需要修改工厂类的代码来适应新的产品。特别是简单工厂模式,如果新增产品就需要修改工厂类中判断逻辑代码,这违法了开闭原则,使得系统的扩展性受到影响。
- 可能导致性能问题:在大量创建对象的情况下,工厂模式可能会引入额外的性能开销。因为每次创建对象都是通过工厂类进行的,这可能会增加不必要的调用和判断逻辑。
2. 实现
2.1 简单工厂模式(Simple Factory Pattern)
定义:
该模式对对象创建管理方式最为简单,因为其仅仅简单的对不同类对象的创建进行了一层薄薄的封装。该模式根据传入的参数动态决定应该创建哪一个产品类的实例。 在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为「静态工厂方法」模式。
UML类图:
代码实现
csharp
//AbstractProduct
public interface Animal {
void eat();
}
//Product1
public class Dog implements Animal {
public Dog() {
this.eat();
}
@Override
public void eat() {
System.out.println("eat bone!");
}
}
//Product2
public class Cat implements Animal {
public Cat() {
this.eat();
}
@Override
public void eat() {
System.out.println("eat fish!");
}
}
//Factory class
public class Factory {
public static Animal createAnimal(String animalType) {
if(animalType.equalsIgnoreCase("Dog")){
return new Dog();
}else if(animalType.equalsIgnoreCase("Cat")) {
return new Cat();
}
return null;
}
}
//use
public class Demo {
public static void main(String[] arg) {
Animal dog = Factory.createAnimal("dog"); // eat bone!
Cat cat = (Cat)Factory.createAnimal("cat"); // eat fish!
}
}
简单工厂模式的优点在于,只需要传入一个正确的参数,就可以获取所需要的对象,而无需知道其创建细节。然而,它的缺点也较为明显:工厂类的职责相对过重,当需要增加新的产品时,可能需要修改工厂类的判断逻辑,这违背了开闭原则。 简单工厂模式适用于工厂类负责创建的对象比较少,且客户端只需要知道传入工厂类的参数,对于如何创建对象(逻辑)不关心的场景。在实际应用中,需要根据具体需求来评估是否使用简单工厂模式。
2.2 工厂方法模式(Factory Method Pattern)
定义:
和简单工厂模式中工厂负责生产所有产品相比,工厂方法模式将生成具体产品的任务分发给具体的产品工厂,也就是定义一个抽象工厂,其定义了产品的生产接口,但不负责具体的产品,将生产任务交给不同的派生类工厂。这样不用通过指定类型来创建对象了。
UML类图:
代码实现
java
//Abstract Product
public interface Animal {
void eat();
}
//Product1
public class Dog implements Animal {
public Dog() {
this.eat();
}
@Override
public void eat() {
System.out.println("eat bone!");
}
}
//Product2
public class Cat implements Animal {
public Cat() {
this.eat();
}
@Override
public void eat() {
System.out.println("eat fish!");
}
}
//Abstract Factory
public interface AnimalFactory {
Animal createAnimal();
}
//Concrete Factory 1
public class DogFactory implements AnimalFactory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
//Concrete Factory 2
public class CatFactory implements AnimalFactory {
@Override
public Animal createAnimal() {
return new Cat();
}
}
//use
public class Demo {
public static void main(String[] arg) {
AnimalFactory dogFactory = new DogFactory();
Animal dog = dogFactory.createAnimal();
dog.eat(); // eat bone!
AnimalFactory catFactory = new CatFactory();
Animal cat = catFactory.createAnimal();
cat.eat(); // eat fish!
}
}
客户端代码只需要使用对应的工厂来创建动物对象,而无需关心具体的创建细节。这样可以增加代码的灵活性和可维护性,当需要添加新的动物类型时,只需要添加新的动物类和对应的工厂类即可,而无需修改客户端代码。 工厂方法模式也存在一些潜在的问题。例如,当产品类较多时,会产生大量的工厂类,增加了系统的复杂度。此外,由于引入了抽象层,可能会增加一些额外的开销。 总的来说,工厂方法模式在需要动态决定创建哪种对象,或者需要隐藏对象创建的具体过程时非常有用。但在使用时,需要权衡其带来的好处和可能带来的复杂性增加。
2.3 抽象工厂模式(Abstract Factory Pattern)
定义:
抽象工厂模式围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂。它提供了一种创建对象的最佳方式,提供了一个创建一系列相关或者相互依赖对象的接口,而无需知道他们的具体类。 在抽象工厂模式中,每个具体工厂都提供了多个工厂方法用于创建多种不同类型的具体对象,这些被创建的对象就构成一个族。每个具体工厂类可以创建多个具体产品类的实例。
UML类图:
代码实现
java
// 抽象产品:手机
public interface Phone {
void display();
}
// 抽象产品:平板
public interface Tablet {
void showSpecs();
}
// 抽象工厂
public interface ElectronicsFactory {
Phone createPhone();
Tablet createTablet();
}
// 高端产品族:手机
public class HighEndPhone implements Phone {
@Override
public void display() {
System.out.println("显示高端手机信息");
}
}
// 高端产品族:平板
public class HighEndTablet implements Tablet {
@Override
public void showSpecs() {
System.out.println("显示高端平板规格");
}
}
// 高端产品族工厂
public class HighEndElectronicsFactory implements ElectronicsFactory {
@Override
public Phone createPhone() {
return new HighEndPhone();
}
@Override
public Tablet createTablet() {
return new HighEndTablet();
}
}
// 中端产品族:手机、平板
public class MidRangePhone implements Phone {
@Override
public void display() {
System.out.println("显示中端手机信息");
}
}
// 高端产品族:平板
public class HighEndTablet implements Tablet {
@Override
public void showSpecs() {
System.out.println("显示高端平板规格");
}
}
// 中端产品族工厂
public class MidRangeElectronicsFactory implements ElectronicsFactory {
@Override
public Phone createPhone() {
return new MidRangePhone();
}
@Override
public Tablet createTablet() {
return new MidEndTablet();
}
}
public class Client {
public static void main(String[] args) {
// 使用高端产品族工厂
ElectronicsFactory highEndFactory = new HighEndElectronicsFactory();
Phone highEndPhone = highEndFactory.createPhone();
Tablet highEndTablet = highEndFactory.createTablet();
highEndPhone.display();
highEndTablet.showSpecs();
// 使用中端产品族工厂
ElectronicsFactory midRangeFactory = new MidRangeElectronicsFactory();
Phone midEndPhone = midRangeFactory.createPhone();
Tablet midEndTablet = midRangeFactory.createTablet();
midEndPhone.display();
midEndTablet.showSpecs();
}
}
在这个例子中,我们有两个产品族(高端和中端),每个产品族都有自己的工厂来创建属于该族的产品。客户端代码通过抽象工厂接口与具体工厂交互,从而可以很容易地切换使用不同的产品族,而无需修改客户端代码。这展示了抽象工厂模式如何提供产品族的创建接口,并使得增加新的产品族变得更加容易。
请注意,抽象工厂模式允许客户端使用不同的产品族,但如果需要向产品族中添加新的产品类型(例如,添加智能手表),那么所有相关的工厂类都需要进行修改,这违背了开闭原则中的"对修改封闭"部分。因此,在设计时需要权衡抽象工厂模式的优点和缺点。