簡單工廠模式
在我們平時的編碼過程中,我們會經常使用到new,而當我們使用new的時候,就會想到"具體",因爲我們的確是在實例化一個具體類,這樣子,可能會導致代碼更脆弱。比如說,我們可能會寫出以下代碼:
Duck duck;
if(picnic){
duck = new MallardDuck();
}else if (hunting){
duck = new DecoyDuck();
}else if(inBathTub){
duck = new RubberDuck();
}
當出現這種代碼時,一旦需求有變化,就必須重新打開這段代碼進行檢查和修改。通常這樣修改過的diam將造成部分系統更難維護和更新,而且更容易犯錯。
披薩的例子
當我們訂購一個披薩的時候,我們可能會寫出如下代碼:
public class PizzaStore{
Pizza orderPizza(String type){
Pizza pizza;
//根據披薩的類型,我們實例化正確的具體類
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
但是一旦我們pizza的種類變化,我們就不得不修改if else語句。因此我們需要把這一部分進行封裝。
建立簡單工廠
public class SimplePizzaFactory{
public Pizza createPizza(String type){
Pizza pizza = null;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}else if(type.equals("pepperoni")){
pizza = new PepperoniPizza();
}
return pizza;
}
}
除此之外,我們還可以將工廠定義爲靜態方法,這是我們常見的一個技巧,因爲不需要使用創建對象的方法來實例化對象,但是這也有確定,不能通過集成來改變創建方法的行爲。
我們的PizzaStore變成了如下:
public class PizzaStore{
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
Pizza orderPizza(String type){
Pizza pizza;
pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
定義簡單工廠
簡單工廠其實不是一個設計模式,它比較像是一種編程習慣,但是我們會經常用到它。
工廠模式
假設我們又增加了新的需求,我們的pizza店需要生產更多的披薩,我們需要根據不同區域的口味,生成不同種類的披薩,這時我們的PizzaStore就會變成這樣
這樣就會有越來越多的工廠,分別產生不同口味的披薩。因此,我們可以通過創建多個“披薩店”分別製造不同口味的披薩,除此之外,我們加工披薩的流程也會產生差異,因此我們需要對我們的pizzaStore做出些改變,將加工pizza的流程和pizza的製作流程捆綁在一起的同時又具有一定彈性。
代碼
我們的PizzaStore現在變成了這樣:
PizzaStore.java
public abstract class PizzaStore {
// 原本是由一個類類負責所有具體類的實例化,現在變成由一堆子類來負責實例化
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);//工廠方法是抽象的,所以依賴子類來處理對象的創建。 工廠方法必須返回一個對象
}
NYPizzaStore.java
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")){
return new NYStyleCheesePizza();
}else if (type.equals("veggie")){
return new NYStyleVeggiePizza();
}else {
return null;
}
}
}
ChicagoPizzaStore.java
public class ChicagoPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")){
return new ChicagoStyleCheesePizza();
}else {
return null;
}
}
}
我們讓PizzaStore的各個子類負責定義自己的createPizza()方法,所以我們會得到一些PizzaStore具體的子類。
那子類是如何做決定的呢?當orderPizza()調用createPizza()方法的時候,某個披薩店的子類(NYStylePizzaStore或者ChicagoStylePizzaStore)來負責創建披薩。
下面,我們看一下我們的Pizza類:
Pizza.java
import java.util.ArrayList;
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();
void prepare(){
System.out.println("Preparing " + name );
System.out.println("Tossing dough...");
System.out.println("Ading sauce...");
System.out.println("Adding toppings: ");
for (int i = 0; i < toppings.size(); i++){
System.out.println(" " + toppings.get(i));
}
}
void bake(){
System.out.println("Bake for 25 minutes at 350");
}
void cut(){
System.out.println("Cutting the pizza into diagonal slices");
}
void box(){
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
}
現在我們需要一些具體子類,來定義紐約和芝加哥風味的芝士披薩。
芝加哥風味的:
ChicagoStyleCheesePizza.java
public class ChicagoStyleCheesePizza extends Pizza {
public ChicagoStyleCheesePizza(){
name = "Chicago Style Deep Dish Cheese Pizzza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
}
void cut(){
System.out.println("Cutting the pizza into square slices");
}
}
紐約風味的
NYStyleCheesePizza.java
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza(){
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Cruse Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
NYStyleVeggiePizza.java
public class NYStyleVeggiePizza extends Pizza {
public NYStyleVeggiePizza(){
name = "NY Style Sauce and Veggie Pizza";
dough = "Thin Cruse Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Veggie");
}
}
測試類:
public class PizzaTestDrive {
public static void main(String[] args){
PizzaStore nyStore = new NYPizzaStore();
PizzaStore chicagoStore = new ChicagoPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a "+ pizza.getName() + "\n");
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel Ordered a "+ pizza.getName() + "\n");
}
}
工廠模式類圖
創建者類:
所有工廠模式都是用來封裝對象的創建。工廠模式通過讓子類決定該創建對象是什麼,來達到將對象創建的過程封裝的目的。
PizzaStore裏面定義了一個抽象的工廠方法createPizza(),讓子類實現此方法,來製造產品。這些子類稱爲具體創建者。
產品類:
上圖這些就是具體的產品。
定義工廠模式
工廠方法模式定義了一個創建對象的接口,但由子類決定要實例化的類是哪一個。工廠方法讓類吧實例化推遲到子類。
工廠模式類圖
所有的產品必須實現這個共同的接口Product,這樣,使用這些產品的類就可以引用這個接口,而不是具體類。
Creator是一個類,它實現了所有操縱產品的方法,但是它不實現工廠方法。Creator的子類必須實現factoryMethod這個抽象方法,來實際製造出產品。ConcreteCreator類負責創建一個或多個具體的ConcreteProduct。
工廠模式和簡單工廠比較
簡單工廠的做法,可以將對象的創建封裝起來,在一個地方都處理完了;但是工廠方法只是提供了一個框架,由子類決定要如何實現。
依賴倒置原則
不能讓高層組件依賴底層組件,無論是高層組件還是底層組件,都應該依賴於抽象。
抽象工廠
我們的披薩製造過程中是由不同的原料組成的,紐約風味的披薩使用一組原料,而芝加哥使用另一組原料。每一種原料都包含一種棉短,一種醬料,一種芝士以及一種海鮮。因此,我們需要處理一下這些原料。
建造原料工廠
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}
在這個接口中,每個原料都有一個對應的方法創建該原料。我們要做的是,爲每一種區域都建造一個工廠,比如說,我們先創建紐約原料工廠。
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThinCrustDough();
}
@Override
public Sauce createSauce() {
return new MarinaraSauce();
}
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
@Override
public Veggies[] createVeggies() {
Viggies viggies[] = {new Garlic(), new Onion(),new MushRoom()};
return viggies;
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
return new FreshClams();
}
}
重做pizza
import java.util.ArrayList;
public abstract class Pizza {
String name;
Dough dough;
Sause sauce;
ArrayList toppings = new ArrayList();
abstract void prepare();
void bake(){
System.out.println("Bake for 25 minutes at 350");
}
void cut(){
System.out.println("Cutting the pizza into diagonal slices");
}
void box(){
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
}
重做CheesePizza
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory = ingredientFactory;
}
@Override
void prepare() {
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
}
}
再看我們的披薩店
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
// 紐約店會用到紐約披薩原料工廠,由該工廠負責生產所有紐約風味比薩所需的原料。
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (type.equals("cheese")){
pizza = new CheesePizza(ingredientFactory);
return pizza;
}else if (type.equals("veggie")){
// TODO
}
}
}
我們公共抽象工廠所提供的接口,可以創建產品家族,利用這個接口,我們的代碼將從實際的工廠解耦。
定義抽象工廠模式
抽象工廠模式提供了一個接口,用於創建相關或依賴對象的家族,而不需要明確指明具體類。
比較工廠方法和抽象工廠
工廠方法和抽象工廠都是負責創建對象,但是工廠方法使用的是繼承,但是抽象工廠使用的是對象的組合。這就意味着,利用工廠方法創建對象,需要擴展一個類,並覆蓋他的工廠方法。但是抽象工廠提供了一個用來創建一個產品家族的抽象類型,這個類型的子類定義了產品被產生的方法。