天天看點

設計之禅——工廠方法

一、工廠概覽

我們生活中有許許多多的工廠,為商家提供産品,而我們開發者口中所談論的工廠方法模式産生靈感也是來源于此,結合生活中的執行個體我們可以非常輕松的了解該模式。它是一種建立型設計模式,也是項目開發中用的最多的設計模式之一,用于對象的建立。

二、走進工廠

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。