場景
(本例子來自于Head First 設計模式)
假如我們現在經營著一家披薩店,那么我們需要提供多種不同口味的披薩供用戶選擇,那么我們可能會這樣做:
enum PizzaEnum{
CHEESE("奶酪"),
VEGGIE("蔬菜");
public String getItem() {
return item;
}
PizzaEnum(String item) {
this.item = item;
}
private String item;
static PizzaEnum fromItem(String item){
for (PizzaEnum pizzaEnum : PizzaEnum.values()){
if (pizzaEnum.item.equals(item))
return pizzaEnum;
}
throw new IllegalArgumentException("沒有找到對應口味的披薩");
}
}
這個枚舉類就代表披薩的不同口味了,那么有了不同口味我們還需要有制作的流程:
class Pizza{
void prepare(){
System.out.println(name()+" 準備制作");
}
void bake(){
System.out.println(name()+" 開始烘烤");
}
private String name(){
return this.getClass().getName();
}
}
這樣就可以把自己的披薩賣給用戶了,當然用戶是可以自己挑選那種口味的披薩,所以還需要讓用戶來挑:
class PizzaStore{
void orderPizza(PizzaEnum item){
Pizza pizza;
switch (item){
case CHEESE:
pizza = new CheesePizza();
case VEGGIE:
pizza = new VeggiePizza();
default:
pizza = new Pizza();
pizza.prepare();
pizza.bake();
System.out.println("披薩制作完成!");
}
}
這里CheesePizza
和VeggiePizza
就代表制作不同口味的披薩了,那么我們的披薩店整體就大致完成了,可以開始售賣了.
如果某一天我們的廚師有了新想法,想要做一種新口味的披薩要怎么辦呢?肯定就是在orderPizza
里面添加新的條件,再添加對應的類;但是這種方式是不是比較麻煩呢?每次有新口味就需要在訂披薩的方法里面添加新的條件,這樣是不是違反了設計程序的單一職責呢,這個類理應來說只負責做披薩就可以了,具體要做哪種口味的其實我們可以交給其他方法或類來做,這就是工廠方法發揮的時候了,我們定義一個方法:
Pizza createPizza(PizzaEnum item) {
switch (item){
case CHEESE:
return new CheesePizza();
case VEGGIE:
return new VeggiePizza();
default:
return new Pizza();
}
}
可以看出,這個方法的功能就是根據參數返回對應的一個披薩的實例,而orderPizza
就不用操心要哪種口味的,只需利用一個抽象類表達所有口味的披薩即可,而且有新口味的話只需要在createPizza
方法中添加新的條件即可.
口味的問題解決了,那么如果某一天有其他地方的人想加盟我們的披薩店,且他們自己可能由于位置原因有他們口味的披薩,就像中國四川一般喜歡吃辣,云南較為清淡,為了適應不同的地方,我們需要做不同口味的披薩,那要怎么做?
其實我們可以按照前面學習的裝飾者模式,設置一個基礎類作為抽象類,有基本的功能,不同地方都繼承自這個類,有口味需求的話只需要重寫相應的方法即可,那么總結起來我們可以把這個場景寫成下面這樣:
/**
* 抽象披薩店,只具有返回不同披薩的抽象方法和訂披薩的基本方法
*/
abstract class PizzaStore{
abstract Pizza createPizza(PizzaEnum item);
void orderPizza(PizzaEnum item){
Pizza pizza = createPizza(item);
pizza.prepare();
pizza.bake();
System.out.println("披薩制作完成!");
}
}
/**
* 紐約開的披薩店,可以看出紐約店有他們自己口味的披薩
*/
class NYPizzaStore extends PizzaStore{
@Override
Pizza createPizza(PizzaEnum item) {
switch (item){
case CHEESE:
return new NYCheesePizza();
case VEGGIE:
return new NYVeggiePizza();
default:
return new Pizza();
}
}
}
/**
* 華盛頓的店也是同樣道理
*/
class WSPizzaStore extends PizzaStore{
@Override Pizza createPizza(PizzaEnum item) {
switch (item){
case CHEESE:
return new WSCheesePizza();
case VEGGIE:
return new WSVeggiePizza();
default:
return new Pizza();
}
}
}
/**
* 這就是我們的披薩類
*/
class Pizza{
void prepare(){
System.out.println(name()+" 準備制作");
}
void bake(){
System.out.println(name()+" 開始烘烤");
}
private String name(){
return this.getClass().getName();
}
}
/**
* 作為學習,這些類里面就沒有寫其他東西了
*/
class NYCheesePizza extends Pizza{
}
class NYVeggiePizza extends Pizza{
}
class WSVeggiePizza extends Pizza{
}
class WSCheesePizza extends Pizza{
}
/**
* 披薩的口味
*/
enum PizzaEnum{
CHEESE("奶酪"),
VEGGIE("蔬菜");
public String getItem() {
return item;
}
PizzaEnum(String item) {
this.item = item;
}
private String item;
static PizzaEnum fromItem(String item){
for (PizzaEnum pizzaEnum : PizzaEnum.values()){
if (pizzaEnum.item.equals(item))
return pizzaEnum;
}
throw new IllegalArgumentException("沒有找到對應口味的披薩");
}
}
來做下實驗:
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("請輸入您想去的城市:WS/NY");
String name = in.nextLine();
PizzaStore pizzaStore;
if (name.equals("WS")){
pizzaStore = new WSPizzaStore();
}else {
pizzaStore = new NYPizzaStore();
}
System.out.println("請輸入您想吃的披薩類型:cheese/veggie");
String item = in.nextLine();
pizzaStore.orderPizza(PizzaEnum.fromItem(item));
}
結果:
看到結果跟我們所期望的一樣,接下來正式介紹一下工廠方法
定義
工廠方法模式(FACTORY METHOD)是一種常用的對象創建型設計模式,此模式的核心精神是封裝類中不變的部分,提取其中個性化善變的部分為獨立類,通過依賴注入以達到解耦、復用和方便后期維護拓展的目的。它的核心結構有四個角色,分別是抽象工廠;具體工廠;抽象產品;具體產品
定義里面提到了依賴注入,相關的還有一個叫做控制反轉(IoC)的東西,都簡單介紹下:
- 依賴注入(DI):Dependency Injection,其實與工廠方法定義很像,組件不負責查找資源或其他依賴的對象,這個工作交給IoC容器來做,由容器來返回需要的對象
- 控制反轉(Ioc):Inversion of Control,指的就是上面的容器,本來創建對象的工作是組件自己實例化的,現在我們利用IoC容器,將這個事情交給固定的容器來負責,組件將更多精力放在其他的邏輯上面,管理起來更方便,不會時常出現一些忘記初始化的錯誤了
Spring的核心思想就是依賴注入和控制反轉,通過定義Bean將實例化Bean的操作交給配置文件來管理,其他類需要的時候配置文件將bean的實例傳給需要的類即可,關于這方面后續會有相應的筆記記錄
優缺點
優點
- 我們在創建實例時不用關心具體創建了哪種實例,怎么創建的,只需把參數傳入工廠即可
- 很明顯,當我們有新的種類添加進來時只需要在具體工廠里面添加相應的條件即可,滿足"開閉原則"
缺點
- 有新的種類加入時需要添加新的類,數目會較多