设计之禅——工厂方法

时间:2022-07-24
本文章向大家介绍设计之禅——工厂方法,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、工厂概览

我们生活中有许许多多的工厂,为商家提供产品,而我们开发者口中所谈论的工厂方法模式产生灵感也是来源于此,结合生活中的实例我们可以非常轻松的理解该模式。它是一种创建型设计模式,也是项目开发中用的最多的设计模式之一,用于对象的创建。

二、走进工厂

1. 简单工厂

在我们平时开发时,常常会用到“new”来创建实例,但你是否想过所有的对象都通过我们自己手动创建存在什么问题?比如你的代码中嵌入了类似下面这样的代码:

Car car;
if (benz) {
	car = new Benz();
} else if (bmw) {
	car = new Bmw();
}

如果你对设计原则有一定的了解的话,应该明白这已经违背了依赖倒置原则(针对接口编程,而不是针对实现),并且每当我们需要增加一个品牌的汽车时,你的代码也都面临重新修改一次,这也完全违背了**对扩展开发,对修改关闭(OPEN-CLOSE)**原则,因此我们在编写代码时应该识别会变化的部分并封装出来。针对上述实例我们就可以引入简单工厂模式来实现:

public abstract class Car {

    abstract void makeHead();

    abstract void makeBody();

    abstract void makeTail();

    public void assemble() {
        System.out.println("Assemble all component to car!");
    }

}

首先抽象出car类,所有具体品牌的车继承该抽象类,这一“家族”就是我们需要的产品。接着,我们将创建者的代码封装为一个简单工厂,客户端则通过该工厂来获取产品实例:

public class CarFactory {

    public static Car getCar(String type) throws Exception {
        if (CarType.BENZ.equals(type)) {
            return new Benz();
        } else if (CarType.BMW.equals(type)) {
            return new Bmw();
        } else {
            throw new Exception("No such car!");
        }

    }

}

public class MainClass {

    public static void main(String[] args) throws Exception {
        Car car = CarFactory.getCar(CarType.BENZ);
        car.makeHead();
        car.makeBody();
        car.makeTail();
        car.assemble();
    }

}

这就是简单工厂的实现,将可能会变化的部分独立出来,增加新的产品时也不用再去修改客户端的代码,但是在简单工厂方法实现部分仍然违背了OPEN-CLOSE原则,这该如何解决呢?

public static Car getCar2(String className) throws Exception {
        Class<?> clzz = Class.forName(className);

        return (Car) clzz.newInstance();
}

 public static void main(String[] args) throws Exception {
        Car benz = CarFactory.getCar2("cn.dark.simplefactory.Benz");
}

我们只需要利用反射就可以了,这样,简单工厂类就不用再通过类型去判断了,客户端只需要传入所需产品的完全限定名就行,至此,我们增加新的产品时也不用再修改任何的代码了,全部由简单工厂为我们创建实例,可以说是万能的(什么都能生产)。但是,生活中是没有万能工厂的,代码里也不存在完美的设计模式。使用简单工厂模式,客户端必须要传入正确的类型,一旦传入错误就得不到想要的产品了,并且使用反射也会使程序性能下降。所以简单工厂模式在《Head First设计模式》书中也被作者定义为并非一种真正的设计模式,而只是一种编码习惯。下面我们就来实现一个真正的工厂模式。

2. 工厂方法模式

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

根据工厂方法模式的定义我们可以了解到我们需要定义一个工厂的接口,然后为每一个具体的产品定义一个工厂并实现自工厂接口,就好比现实生活中,生产汽车的工厂就只生产汽车,生产自行车的工厂就只生产自行车,这样,每当增加一个新的产品时,我们就只需要再增加一个对应的工厂,而不用再修改以前的类,也符合了OPEN-CLOSE原则。代码如下:

public abstract class Vehicle {

    abstract void makeHead();

    abstract void makeBody();

    abstract void makeTail();

    public void assemble() {
        System.out.println("Assemble all component to vehicle!");
    }

}

public class Car extends Vehicle {

    @Override
    void makeHead() {
        System.out.println("Car's head is created!");
    }

    @Override
    void makeBody() {
        System.out.println("Car's body is created!");
    }

    @Override
    void makeTail() {
        System.out.println("Car's tail is created!");
    }
}

public class Bike extends Vehicle {

    @Override
    void makeHead() {
        System.out.println("Bike's head is created!");
    }

    @Override
    void makeBody() {
        System.out.println("Bike's body is created!");
    }

    @Override
    void makeTail() {
        System.out.println("Bike's tail is created!");
    }
}

上面是产品家族,接着是工厂家族:

public interface Factory {

    Vehicle getCar();

}

public class BikeFactory implements Factory {

    @Override
    public Vehicle getCar() {
        return new Bike();
    }

}

public class CarFactory implements Factory {

    @Override
    public Vehicle getCar() {
        return new Car();
    }

}

public class MainClass {

    public static void main(String[] args) {
        Factory factory = new BikeFactory();
        Vehicle benz = factory.getCar();
        factory = new CarFactory();
        Vehicle bmw = factory.getCar();
    }

}

这就是一个工厂模式的简单实现,看起来这就相当完美了,但是如果一个工厂不只是生产一类产品,而是一组具有同一属性的产品,那工厂模式就不那么适用了,于是,就有了抽象工厂模式。

3. 抽象工厂模式

如上所说,在我们现实生活中往往也是一个工厂会生产多类产品,但这些产品都属于一个品牌下,比如,奔驰工厂会生产汽车,也可能会生产自行车,或者是电动车等等,宝马工厂也是一样。这就需要对工厂进行抽象,而不再是产品。对于上述代码我们可以进行如下的改进,首先还是产品家族:

public interface Vehicle {

}

public abstract class Car implements Vehicle {

}

public abstract class Bike implements Vehicle {

}

public class BenzBike extends Bike {

}

public class BenzCar extends Car {

}

public class BmwBike extends Bike {

}

public class BmwCar extends Car {

}

然后是工厂家族:

public interface Factory {

    Car getCar();

    Bike getBike();

}

public class BmwFactory implements Factory {

    @Override
    public Car getCar() {
        return new BmwCar();
    }

    @Override
    public Bike getBike() {
        return new BmwBike();
    }
}

public class BenzFactory implements Factory {

    @Override
    public Car getCar() {
        return new BenzCar();
    }

    @Override
    public Bike getBike() {
        return new BenzBike();
    }
}

通过代码我们可以明显的发现工厂模式和抽象工厂的区别在哪里,工厂模式中创建工厂的每一个实现对应于每一类产品,一个工厂就只产生这一类产品;而抽象工厂模式中每一个具体的工厂则是对应于具有同一属性的多类产品,每一个工厂都可生产一族产品,但若是从产品种类新增的角度来讲又违背了open-close原则。因此,我们在实际应用中应当根据具体情况而使用,往往一般都是互相搭配使用。最后再看看《Head First设计模式》对于抽象工厂的定义:

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

三、总结

本篇讲述了简单工厂、工厂模式以及抽象工厂模式的具体实现,该类模式在我们实际编码中也是非常常用的,因此了解掌握是必要的。项目完整代码请访问小编github