定義
定義一個(gè)創(chuàng)建對(duì)象的接口够挂,但由子類決定要實(shí)例化的類是哪一個(gè)。工廠方法讓類把實(shí)例化推遲到子類。
使用場(chǎng)景
- 一個(gè)類不能預(yù)期它必須創(chuàng)建的對(duì)象的類
- 一個(gè)類想要它的子類指定它創(chuàng)建的對(duì)象
- 類將責(zé)任委托給幾個(gè)輔助子類之一,并且你想要知道哪個(gè)輔助子類是委托
例子
有個(gè)披薩店毫胜,用戶可以下單定披薩。披薩店首先根據(jù)用戶的選擇披薩(CheesePizza昔瞧、GreekPizza)指蚁,然后按照制作流程進(jìn)行制作,最后把披薩返回給用戶:
public class PizzaStore{
public Pizza orderPizza(String type){
Pizza pizza;
if(type.equals("cheese")){
pizza = new CheesePizza();
}else if(type.equals("greek")){
pizza = new GreekPizza();
}
...
// 制作流程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
我們發(fā)現(xiàn)上面程序中自晰,選擇披薩是一個(gè)變化的部分凝化,而披薩的制作流程是不變的,我們把這部分抽取出來(lái)提煉一個(gè)工廠函數(shù):
// 制作工廠
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();
}
...
return pizza;
}
}
public class PizzaStore{
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory){
this.factory = factory;
}
public Pizza orderPizza(String type){
Pizza pizza;
pizza = factory.createPizza(type);
// 制作流程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
通過(guò)這個(gè)factory酬荞,我們把依據(jù)類型選擇pizza這段邏輯提取出來(lái)搓劫。這里帶來(lái)一個(gè)最簡(jiǎn)單的好處是:所有需要通過(guò)類型選擇pizza的地方都可以調(diào)用這個(gè)方法,比如結(jié)賬混巧、制作pizza的菜單等枪向。但是這還不是工廠模式。
需求進(jìn)一步擴(kuò)展咧党,pizza開(kāi)了很多的分店秘蛔,比如北京、上海傍衡,每個(gè)店就可以制作特定地區(qū)原料的pizza深员。我們可以用下面的方法實(shí)現(xiàn):
// 從北京店定制cheese披薩
BeijingPizzaFactory factory = new BeijingPizzaFactory();
PizzaStore bjStore = new PizzaStore(factory);
bjStore.orderPizza("cheese");
// 從北京店定制greek披薩
ShanghaiPizzaFactory factory = new ShanghaiPizzaFactory();
PizzaStore shStore = new PizzaStore(factory);
shStore.orderPizza("greek");
也可以通過(guò)繼承來(lái)實(shí)現(xiàn):
public abstract class PizzaStore{
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
...
return pizza;
}
abstract Pizza createPizza(String type);
}
// 北京披薩店
public class BeijingPizzaStore extends PizzaStore{
public Pizza createPizza(String type){
if(type.equals("cheese")){
pizza = new BeijingCheesePizza();
}
...
return pizza;
}
}
到了這一步,我們還沒(méi)有實(shí)現(xiàn)pizza類:
public abstract class Pizza{
String name; // 名字
String dough; // 面團(tuán)
String sauce // 醬
void prepare(){
// do prepare
}
void bake(){
System.out.println("bake");
}
void cut(){
System.out.println("cut");
}
void box(){
System.out.println("box");
}
public String getName(){
return name;
}
}
public class BeijingCheesePizza extends Pizza{
public BeijingCheesePizza(){
name = "beijing cheese pizza";
dough = "白面";
sauce = "辣椒醬";
}
}
對(duì)上面的例子做個(gè)測(cè)試:
public class App{
public static void main(String[] args){
BeijingPizzaStore bjStore = new BeijingPizzaStore();
Pizza pizza = bjStore.orderPizza("cheese")
}
}
分析
工廠模式都用來(lái)封裝對(duì)象的創(chuàng)建蛙埂。工廠方法模式通過(guò)讓子類決定該創(chuàng)建的對(duì)象是什么倦畅,來(lái)達(dá)到將對(duì)象創(chuàng)建的過(guò)程封裝的目的。
上面的例子中绣的,pizzaStore
是抽象創(chuàng)建類叠赐;BeijingPizzaStore
是具體創(chuàng)建類,它的createPizza
方法正是工廠方法屡江,用來(lái)制作產(chǎn)品芭概;Pizza
是工廠的產(chǎn)品類。
工廠模式體現(xiàn)了設(shè)計(jì)模式一個(gè)很重要的原則(依賴倒置原則):要依賴抽象惩嘉,不要依賴具體類谈山。pizzaStore
依賴了Pizza
這個(gè)抽象類,而不是CheesePizza
宏怔、GreekPizza
這些具體類奏路。
要開(kāi)一個(gè)披薩店,我們首先想到的是要能制作各種披薩臊诊,如芝士披薩鸽粉、熟食披薩等。但是如果披薩店依賴這些具體的披薩那就麻煩了(第一段程序)抓艳;反過(guò)來(lái)触机,我們把所有具體的披薩抽象成一個(gè)pizza
類,那么和披薩店依賴的是這個(gè)pizza
類玷或,而不是具體的披薩了儡首。
進(jìn)一步延伸
接上面披薩店的需求。不同的地方原料不一樣偏友,比如北方人面食多蔬胯,因此披薩用面來(lái)做;南方人喜歡吃米飯位他,因此披薩用大米來(lái)做(胡扯..)氛濒,因此我們提供一個(gè)接口,負(fù)責(zé)創(chuàng)建所有的原料:
public interface PizzaIngredientFactory{
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
}
那么北京的披薩店制作披薩時(shí)需實(shí)現(xiàn)這個(gè)接口鹅髓,用于準(zhǔn)備披薩的材料:
public class BeijingPizzaIngredientFactory implements PizzaIngredientFactory{
public Dough createDough(){
return new BaiMianDough();
}
public Sauce createSauce(){
return new LajiaoSauce();
}
}
北京的披薩店類需做修改:
public abstract class Pizza{
String name; // 名字
Dough dough; // 面團(tuán)
Sauce sauce // 醬
abstract void prepare(); // 由子類實(shí)現(xiàn) 準(zhǔn)備原料
...
}
// 具體的披薩 和 原料關(guān)聯(lián)舞竿,原料的實(shí)現(xiàn)由工廠方法提供
public class CheesePizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory = ingredientFactory;
}
void prepare(){
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
public class BeijingPizzaStore extends PizzaStore{
protected Pizza createPizza(String item){
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new BeijingPizzaIngredientFactory();
if(item.equals("cheese")){
pizza = new CheesePizza(ingredientFactory);
}
...
return pizza;
}
}
抽象工廠類
抽象工廠類模式提供一個(gè)接口,用于創(chuàng)建相關(guān)或依賴對(duì)象的家族窿冯,而不需要明確指定具體類骗奖。由于不需要知道實(shí)際產(chǎn)出的具體產(chǎn)品(上面例子中BaiMianDough、LajiaoSauce)是什么醒串,這樣客戶就從具體的產(chǎn)品中解耦执桌。
兩者區(qū)別
工廠方法: 用來(lái)創(chuàng)建對(duì)象,具體說(shuō)是通過(guò)子類創(chuàng)建對(duì)象厦凤”撬保客戶只需要知道他們所使用的抽象類型即可,而由子類負(fù)責(zé)決定具體類型较鼓。負(fù)責(zé)將客戶從具體類型中解耦椎木。
抽象工廠方法:創(chuàng)建一個(gè)產(chǎn)品家族的抽象類型,這個(gè)類型的子類定義了產(chǎn)品被產(chǎn)生的方法博烂。
簡(jiǎn)單點(diǎn)的例子
參考游戲《鐵匠》:
// 鐵匠能夠制作武器
public interface Blacksmith {
Weapon manufactureWeapon(WeaponType weaponType);
}
// elf制作精靈族的武器
public class ElfBlacksmith implements Blacksmith {
public Weapon manufactureWeapon(WeaponType weaponType) {
return new ElfWeapon(weaponType);
}
}
// orc制作獸族的武器
public class OrcBlacksmith implements Blacksmith {
public Weapon manufactureWeapon(WeaponType weaponType) {
return new OrcWeapon(weaponType);
}
}
// 武器與種族和類別有關(guān)
public interface Weapon {
WeaponType getWeaponType();
}
// 精靈族的武器
public class ElfWeapon implements Weapon {
private WeaponType weaponType;
public ElfWeapon(WeaponType weaponType) {
this.weaponType = weaponType;
}
public String toString() {
return "Elven " + weaponType;
}
public WeaponType getWeaponType() {
return weaponType;
}
}
// 獸族的武器
public class OrcWeapon implements Weapon {
private WeaponType weaponType;
public OrcWeapon(WeaponType weaponType) {
this.weaponType = weaponType;
}
public String toString() {
return "Orcish " + weaponType;
}
public WeaponType getWeaponType() {
return weaponType;
}
}
// 武器本身分為 短劍香椎、矛、斧三種
public enum WeaponType {
SHORT_SWORD("short sword"), SPEAR("spear"), AXE("axe"), UNDEFINED("");
private String title;
WeaponType(String title) {
this.title = title;
}
public String toString() {
return title;
}
}
// 測(cè)試 調(diào)用的是鐵匠這個(gè)基類禽篱,通過(guò)指向不同的對(duì)象畜伐,定義其行為
public class App {
private final Blacksmith blacksmith;
public App(Blacksmith blacksmith) {
this.blacksmith = blacksmith;
}
public static void main(String[] args) {
// 用獸族的武器去戰(zhàn)爭(zhēng)
App app = new App(new OrcBlacksmith());
app.manufactureWeapons();
// 用精靈族的武器去戰(zhàn)爭(zhēng)
app = new App(new ElfBlacksmith());
app.manufactureWeapons();
}
private void manufactureWeapons() {
Weapon weapon;
weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); // 矛
System.out.println(weapon);
weapon = blacksmith.manufactureWeapon(WeaponType.AXE); // 斧
System.out.println(weapon);
}
}