概念
工厂方法是一种创建型设计模式,它定义了一个用于创建对象的接口,但将实际的实例化推迟到子类中。这样,子类可以决定要实例化的具体类。工厂方法模式允许一个类在运行时动态创建对象,而不需要直接指定它们的具体类。这种模式有助于降低代码的耦合度,使得代码更易于扩展和维护。
举例说明
举个例子,考虑一个简单的工厂方法模式场景:假设我们有一个抽象类Animal,它有一个抽象的方法createAnimal()用于创建动物对象。然后我们有两个具体的子类Dog和Cat,它们分别实现了createAnimal()方法以创建具体的狗和猫对象。
当我们需要创建一个动物对象时,我们可以通过调用工厂方法createAnimal(),而不需要直接实例化Dog或Cat类。这样,我们可以根据需要轻松地添加新的动物类型,而不会影响现有的代码。
原因
这是因为工厂方法模式将对象的创建过程封装在一个接口或抽象类中,而具体的实例化操作则由子类来实现。通过这种方式,客户端代码只需要调用工厂方法来创建对象,而无需关心具体的实现细节。这种解耦的设计使得系统更加灵活和可扩展。
当需要添加新的动物类型时,我们只需要创建一个新的具体子类来实现工厂方法,并在其中实例化新的动物对象。由于客户端代码只依赖于抽象的工厂方法接口,而不是具体的动物类,因此添加新的动物类型不会影响现有的代码。这种设计符合开闭原则,即对扩展开放,对修改关闭。
代码实例
假设我们有一个动物园应用程序,需要创建不同类型的动物对象,如狗、猫和鸟。我们可以使用工厂方法模式来实现这个场景。
首先,我们定义一个抽象类Animal,其中包含一个抽象的工厂方法createAnimal()用于创建动物对象:
abstract class Animal {
public abstract void makeSound();
}
然后,我们创建具体的动物类,如Dog、Cat和Bird,它们分别继承自Animal类并实现makeSound()方法:
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
class Bird extends Animal {
@Override
public void makeSound() {
System.out.println("Bird chirps");
}
}
接下来,我们定义一个抽象工厂类AnimalFactory,其中包含一个工厂方法createAnimal()用于创建动物对象:
abstract class AnimalFactory {
public abstract Animal createAnimal();
}
然后,我们创建具体的工厂类,如DogFactory、CatFactory和BirdFactory,它们分别继承自AnimalFactory类并实现createAnimal()方法来实例化具体的动物对象:
class DogFactory extends AnimalFactory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
class CatFactory extends AnimalFactory {
@Override
public Animal createAnimal() {
return new Cat();
}
}
class BirdFactory extends AnimalFactory {
@Override
public Animal createAnimal() {
return new Bird();
}
}
最后,我们可以在客户端代码中使用工厂方法来创建不同类型的动物对象,而不需要直接实例化具体的动物类:
AnimalFactory dogFactory = new DogFactory();
Animal dog = dogFactory.createAnimal();
dog.makeSound(); // Output: Dog barks
AnimalFactory catFactory = new CatFactory();
Animal cat = catFactory.createAnimal();
cat.makeSound(); // Output: Cat meows
AnimalFactory birdFactory = new BirdFactory();
Animal bird = birdFactory.createAnimal();
bird.makeSound(); // Output: Bird chirps
在上面的示例中,客户端代码通过AnimalFactory接口与具体的工厂类DogFactory进行交互,请求创建一个Dog对象。具体的工厂类负责实际创建Dog对象并返回给客户端。
通过这种方式,客户端代码与具体的工厂类解耦,客户端只需要知道如何与工厂接口进行交互,而不需要关心具体的工厂类是哪个。这种设计使得系统更加灵活,可以轻松地替换具体的工厂类而不影响客户端代码。
思考
传统没有工厂方法的代码会怎么做呢?
在没有使用工厂方法模式的情况下,客户端代码通常会直接实例化具体的动物类来创建对象。这种方式会导致客户端代码与具体的动物类耦合在一起,使得系统的扩展和维护变得困难。
举例来说,如果没有工厂方法模式,客户端代码可能会像这样直接实例化动物对象:
Animal dog = new Dog();
dog.makeSound(); // Output: Dog barks
Animal cat = new Cat();
cat.makeSound(); // Output: Cat meows
Animal bird = new Bird();
bird.makeSound(); // Output: Bird chirps
在这种情况下,如果需要添加新的动物类型,比如添加一种名为Fish的动物类,客户端代码就需要修改并添加新的实例化代码:
Animal fish = new Fish();
fish.makeSound(); // Output: Fish swims
两者区别
在没有使用工厂方法模式的情况下,客户端代码直接实例化具体的动物类来创建对象。因此,如果需要添加新的动物类型,比如添加一种名为Fish的动物类,客户端代码就需要修改并添加新的实例化代码的原因如下:
-
直接依赖具体类:客户端代码直接依赖于具体的动物类,例如Dog、Cat、Bird等。当需要添加新的动物类型时,客户端代码必须直接修改并添加新的实例化代码来实例化新的动物类。
-
违反开闭原则:开闭原则要求系统对扩展开放,对修改关闭。直接在客户端代码中添加新的实例化代码违反了这一原则,因为每次添加新的动物类型都需要修改客户端代码。
-
缺乏抽象层:没有工厂方法模式的抽象工厂接口或类来封装对象的创建过程。客户端代码直接实例化具体类,导致系统缺乏抽象层,难以实现对象创建过程的解耦和灵活性。
因此,没有使用工厂方法模式时,客户端代码需要直接修改并添加新的实例化代码来创建新的对象,这会导致代码的脆弱性和可维护性下降。相比之下,工厂方法模式通过将对象的创建过程封装在工厂类中,使得系统更加灵活和可扩展,客户端代码只需要与工厂接口交互而不需要直接依赖具体类,从而避免了频繁修改客户端代码的情况。
public class Zoo {
public static void main(String[] args) {
// 直接在客户端代码中实例化具体的动物类
Dog dog = new Dog();
Cat cat = new Cat();
// 客户端代码需要修改并添加新的实例化代码来添加新的动物类型
Bird bird = new Bird();
Fish fish = new Fish();
}
}
class Dog {
public Dog() {
System.out.println("Dog created");
}
}
class Cat {
public Cat() {
System.out.println("Cat created");
}
}
class Bird {
public Bird() {
System.out.println("Bird created");
}
}
class Fish {
public Fish() {
System.out.println("Fish created");
}
}
在上面的示例中,客户端代码直接实例化了具体的动物类(Dog、Cat、Bird、Fish)来创建对象。如果需要添加新的动物类型,比如Fish类,客户端代码就需要修改并添加新的实例化代码,违反了开闭原则并导致代码的脆弱性。
而通过使用工厂方法模式,可以将对象的创建过程封装在工厂类中,客户端代码只需要与工厂接口交互来请求创建对象,而不需要直接依赖具体的类。这样就可以避免频繁修改客户端代码的情况,提高系统的灵活性和可维护性。
客户端不需要直接实例化具体的产品类,而是通过工厂方法来创建对象。工厂方法模式将对象的创建延迟到子类中,让子类决定具体要实例化的类。这样客户端只需要与工厂接口或抽象工厂类交互,而不需要直接依赖具体的产品类。
举个例子,假设我们有一个动物园应用程序,使用工厂方法模式来创建不同类型的动物对象。下面是一个简单的示例代码:
// 抽象产品类
interface Animal {
void makeSound();
}
// 具体产品类
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Cat meows");
}
}
// 工厂接口
interface AnimalFactory {
Animal createAnimal();
}
// 具体工厂类
class DogFactory implements AnimalFactory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
class CatFactory implements AnimalFactory {
@Override
public Animal createAnimal() {
return new Cat();
}
}
public class Zoo {
public static void main(String[] args) {
// 使用工厂方法创建动物对象
AnimalFactory dogFactory = new DogFactory();
Animal dog = dogFactory.createAnimal();
dog.makeSound();
AnimalFactory catFactory = new CatFactory();
Animal cat = catFactory.createAnimal();
cat.makeSound();
}
}
在上面的示例中,客户端代码通过工厂方法(createAnimal)来创建不同类型的动物对象,而不需要直接实例化具体的产品类。如果需要添加新的动物类型,比如Fish类,只需要创建一个新的具体工厂类(FishFactory)和产品类(Fish),而不需要修改客户端代码。这样就实现了代码的扩展性和灵活性,符合开闭原则。
静态工厂方法
静态工厂方法是一种创建对象的设计模式,它通常是一个静态方法,用于创建并返回特定类型的对象实例。静态工厂方法通常属于工厂方法模式的一种变体,它不需要创建工厂类的实例,而是直接在类中定义静态方法来创建对象。
静态工厂方法具有以下特点:
- 在类中定义静态方法来创建对象,无需实例化工厂类。
- 可以根据参数的不同返回不同类型的对象实例。
- 可以用于封装对象的创建逻辑,隐藏对象的具体实现细节。
- 可以用于实现单例模式、工具类等。
静态工厂方法的优点包括:
- 简化对象的创建过程,客户端代码直接调用静态方法即可获取对象实例。
- 可以根据需要灵活地返回不同类型的对象,提高了代码的灵活性和可扩展性。
- 可以隐藏对象的具体实现细节,降低了客户端代码与具体实现的耦合度。
举例来说,下面是一个简单的示例展示了如何使用静态工厂方法创建对象:
public class TraceFactory {
public static Trace getTrace(String type) {
if (type.equals("file")) {
return new FileTrace();
} else if (type.equals("system")) {
return new SystemTrace();
} else {
return null;
}
}
}
public class Main {
public static void main(String[] args) {
Trace fileTrace = TraceFactory.getTrace("file");
fileTrace.debug("Debug message for file trace");
Trace systemTrace = TraceFactory.getTrace("system");
systemTrace.debug("Debug message for system trace");
}
}
在上面的示例中,TraceFactory类定义了一个静态方法getTrace,根据传入的参数type来创建不同类型的Trace对象(FileTrace或SystemTrace)。客户端代码在Main类中直接调用TraceFactory的静态方法来获取Trace对象实例,而不需要关心具体的创建逻辑。这样就实现了对象的创建和使用的解耦。