1. 簡單工廠 1. 你開了一家披薩店,點披薩的方法可能是這樣: 可以看到,每當你想增加一種披薩類型,就要修改代碼,添加一種if else條件.當有多個系統存在orderPizza的需求時,每個系統都要同時修改他們的代碼.因此,需要將這種實例化具體對象的代碼封裝起來. 這就是簡單工廠方法,他不算一種 ...
1. 簡單工廠
1. 你開了一家披薩店,點披薩的方法可能是這樣:
public Pizza orderPizza(String type) {
Pizza pizza;
if (type.equals("芒果披薩")){
pizza = new MangoPizza();
}else if (type.equals("核桃披薩")){
pizza = new WalnutPizza();
}else if (type.equals("橙皮披薩")){
pizza = new FlavedoPizza();
}else {
pizza = new StarPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
可以看到,每當你想增加一種披薩類型,就要修改代碼,添加一種if else條件.當有多個系統存在orderPizza的需求時,每個系統都要同時修改他們的代碼.因此,需要將這種實例化具體對象的代碼封裝起來.
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;
}
}
public class SimplePizzaFactory {
public Pizza createPizza(String type){
Pizza pizza = null;
if (type.equals("芒果披薩")){
pizza = new MangoPizza();
}else if (type.equals("核桃披薩")){
pizza = new WalnutPizza();
}else if (type.equals("橙皮披薩")){
pizza = new FlavedoPizza();
}else {
pizza = new StarPizza();
}
return pizza;
}
}
這就是簡單工廠方法,他不算一種設計模式,而是一種編程習慣.其要點有二:
1. 將產品定義為抽象類,可以被覆蓋
2. 將具體的產品放到工廠類中來實例化,由工廠創建並返回給客戶,這樣便使得產品的創建邏輯可以解耦合,並增加了復用性
2. 工廠方法
1. 假設你現在有了很多加盟店,每種加盟店根據地區的差異有自己的工廠,但加盟店雖然採用工廠方法創建披薩,但其他部分卻有所不同:烘烤的做法,不切片,使用雜七雜八的盒子...
這時,你想把創建披薩的方法與每家披薩店綁定在一起,讓每家PizzaStore中都使用createPizza()創建pizza,並且這些店還可以擁有一定的製作披薩的自主權.因此,我們需要把PizzaStore和createPizza()定義成抽象的
/**
* 抽象的PizzaStore
*/
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
// 使用自己的方法做披薩
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 將做披薩的工廠方法放到PizzaStore中,工廠方法是抽象的,所以依賴子類來處理對象的創建
public abstract Pizza createPizza(String type);
}
2. 現在由PizzaStore的子類來具體賣披薩
public class BeijingPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String item) {
if (item.equals("橙皮披薩")){
return new FlavedoPizza();
}else {
return new PekingDuckPizza();
}
}
}
public class ChengduPizza extends PizzaStore {
@Override
public Pizza createPizza(String type) {
if (type.equals("芒果披薩")){
return new MangoPizza();
}else {
return new StarPizza();
}
}
}
現在,orderPizza()與具體的Pizza實現了一定的解耦合,決定最終Pizza對象的變成了某個具體的pizza店,而pizza店則由顧客來決定
3. 一些對工廠方法的理解:
1. 工廠方法是抽象的,所以依賴子類來創建對象,這樣便將超類的代碼與具體創建產品的代碼分割開了
2. 工廠方法必須返回一個產品,超類通常中會用到這個產品
3. 工廠方法將產品與創建者抽象為介面,使他們成為平級的存在,具體的產品創建則使用子類來交互
4. 工廠方法將產品實例化的操作推遲到子類,子類的選擇則由創建者來決定
5. 簡單工廠方法更趨向組合,工廠方法則利用抽象和繼承
6. 簡單工廠封裝了對象的創建,一個工廠處理所有的產品,但對同一個產品不可變更其做法,工廠方法則更有彈性,子類通過重寫可以更改產品的創建
3. 依賴倒置原則
下麵是一個簡單粗暴的披薩店:
/**
* 依賴具體對象的PizzaStore
*/
public class PizzaStore {
public Pizza createPizza(String style, String type){
Pizza pizza = null;
if (style.equals("一元檔")){
if (type.equals("芒果披薩")){
pizza = new MangoPizza();
}else if (type.equals("核桃披薩")){
pizza = new WalnutPizza();
}
}else if (style.equals("二元檔")){
if (type.equals("北京烤鴨披薩")){
pizza = new PekingDuckPizza();
}else if (type.equals("橙皮披薩")){
pizza = new FlavedoPizza();
}
}else {
System.out.println("披薩賣完了");
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
可以看到,這個披薩店依賴於每一個具體的披薩對象,每增加一個披薩就要修改一次代碼,增加一個依賴,可以說是非常不好用了
從圖中看到,高層組件直接依賴於低層組件而依賴倒置原則的的內容就是:
要依賴抽象,不要依賴具體類
這個原則說明不管高層還是低層組件都不應該依賴具體類,而應該依賴於抽象.如圖所示:
與之前相比高層組件PizzaStore不在直接依賴於具體的Pizza對象,而是依賴於Pizza介面,而具體的Pizza產品則由被高層組件依賴顛倒成了依賴Pizza介面,這就是一種依賴倒置.總體來看,PizzaStore和Pizza都抽象化了,不用再依賴於具體的類型,符合依賴倒置的設計原則.
有三個指導方針可以讓你更好地遵守依賴倒置原則:
- 變數不可以持有具體類的引用
- 不要讓類派生自具體類
- 不要覆蓋基類中已覆蓋的方法
4. 抽象工廠方法:創建產品家族
與普通的工廠方法比較,抽象工廠方法要顯得重型得多: 工廠方法只創建一種產品,抽象工廠方法則創建一個家族的產品
1. 典型做法是創建一個工廠的介面,這個介面負責創建所有的產品家族成員,如創建一個生產調料家族的工廠:
/**
* 原料工廠的介面,實現這樣的介面,可以隨意組合產出各式各樣的調料
* 每個原料都是一個類
*/
public interface SeasoningFactory {
Chili createChili();
DouBan createDouBan();
Pepper createPepper();
Salt createSalt();
}
/**
* 郫縣調料生產廠
*/
public class PiXianSeasoningFactory implements SeasoningFactory {
/**
* 虎皮青椒
* @return
*/
@Override
public Chili createChili() {
return new HuPiQingJiao();
}
/**
* 郫縣豆瓣
* @return
*/
@Override
public DouBan createDouBan() {
return new PiXianDouBan();
}
/**
* 花椒油
* @return
*/
@Override
public Pepper createPepper() {
return new HuaJiaoYou();
}
/**
* 食用鹽
* @return
*/
@Override
public Salt createSalt() {
return new ShiYongYan();
}
}
/**
* 灌縣調料生產廠
*/
public class GuanXianSeasoningFactory implements SeasoningFactory {
/**
* 虎皮青椒
* @return
*/
@Override
public Chili createChili() {
return new HuPiQingJiao();
}
/**
* 老乾媽豆瓣醬
* @return
*/
@Override
public DouBan createDouBan() {
return new LaoGanMaDouBanJiang();
}
/**
* 花椒油
* @return
*/
@Override
public Pepper createPepper() {
return new HuaJiaoYou();
}
/**
* 食用鹽
* @return
*/
@Override
public Salt createSalt() {
return new ShiYongYan();
}
}
下麵是抽象工廠方法的類圖:
從類圖中可以看出來,抽象工廠方法有一個缺點,就是當添加了一個新產品介面時,要去抽象工廠介面中添加一個方法,這會造成一些麻煩
2. 工廠方法與抽象工廠的一些對比
相同點:
- 都是負責創建對象
- 都能使程式解耦,將程式從創建大量具體對象的泥潭中拉出來
不同點:
- 工廠方法用子類來創建具體對象,利用抽象和繼承.抽象工廠包含了一大堆介面,利用組合的思想來創建對象
- 工廠方法一般用來創建一種產品,抽象工廠則創建一族產品
工廠方法功能簡單,使用輕便.抽象工廠功能強大,使用時會創建大量的類
3. 下麵使用抽象工廠生產的原料來製作披薩
/**
* 原料工廠的介面,實現這樣的介面,可以隨意組合產出各式各樣的調料
* 每個原料都是一個類
*/
public interface SeasoningFactory {
/**
* 辣椒
* @return
*/
Chili createChili();
/**
* 豆瓣
* @return
*/
DouBan createDouBan();
/**
* 花椒
* @return
*/
Pepper createPepper();
/**
* 鹽
* @return
*/
Salt createSalt();
}
/**
* 豆瓣
*/
public abstract class DouBan {
public abstract String sayMyName();
}
public class PiXianDouBan extends DouBan {
@Override
public String sayMyName() {
return "郫縣豆瓣";
}
}
public class LaoGanMaDouBanJiang extends DouBan {
@Override
public String sayMyName() {
return "老乾媽豆瓣醬";
}
}
/**
* 郫縣調料生產廠
*/
public class PiXianSeasoningFactory implements SeasoningFactory {
/**
* 虎皮青椒
* @return
*/
@Override
public Chili createChili() {
return new HuPiQingJiao();
}
/**
* 郫縣豆瓣
* @return
*/
@Override
public DouBan createDouBan() {
return new PiXianDouBan();
}
/**
* 花椒油
* @return
*/
@Override
public Pepper createPepper() {
return new HuaJiaoYou();
}
/**
* 食用鹽
* @return
*/
@Override
public Salt createSalt() {
return new ShiYongYan();
}
}
/**
* 灌縣調料生產廠
*/
public class GuanXianSeasoningFactory implements SeasoningFactory {
/**
* 虎皮青椒
* @return
*/
@Override
public Chili createChili() {
return new HuPiQingJiao();
}
/**
* 老乾媽豆瓣醬
* @return
*/
@Override
public DouBan createDouBan() {
return new LaoGanMaDouBanJiang();
}
/**
* 花椒油
* @return
*/
@Override
public Pepper createPepper() {
return new HuaJiaoYou();
}
/**
* 食用鹽
* @return
*/
@Override
public Salt createSalt() {
return new ShiYongYan();
}
}
/**
* 每一個披薩都有一組抽象工廠生產的原料
*
*/
public abstract class Pizza {
protected String name;
/**
* 辣椒
*/
protected Chili chili;
/**
* 豆瓣
*/
protected DouBan douBan;
/**
* 花椒
*/
protected Pepper pepper;
/**
* 鹽
*/
protected Salt salt;
/**
* 在這個方法中,收集抽象工廠生產的原料
*/
public abstract void prepare();
public void bake(){
System.out.println("烘烤...");
}
public void cut(){
System.out.println("切片...");
}
public void box() {
System.out.println("包裝...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString(){
return "披薩製作完畢:"+name
+"\n生產原料:"+chili.sayMyName()
+"\t"+douBan.sayMyName()
+"\t"+pepper.sayMyName()
+"\t"+salt.sayMyName();
}
}
public class ChengduPizza extends Pizza{
// 調料工廠
SeasoningFactory seasoningFactory;
public ChengduPizza(SeasoningFactory seasoningFactory) {
this.name = "舊成都披薩";
this.seasoningFactory = seasoningFactory;
}
@Override
public void prepare() {
System.out.println("開始準備 "+getName()+":");
chili = seasoningFactory.createChili();
douBan = seasoningFactory.createDouBan();
pepper = seasoningFactory.createPepper();
salt = seasoningFactory.createSalt();
System.out.println("以下原料準備完畢:"+chili.sayMyName()
+","+douBan.sayMyName()+","+pepper.sayMyName()+","+salt.sayMyName());
}
}
public abstract class PizzaStore {
public abstract Pizza createPizza(String type);
}
public class StarBoundPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
Pizza pizza;
SeasoningFactory factoryA = new PiXianSeasoningFactory();
SeasoningFactory factoryB = new GuanXianSeasoningFactory();
if (type.equals("舊成都披薩")){
pizza = new ChengduPizza(factoryA);;
}else if (type.equals("新北京披薩")){
pizza = new PekingPizza(factoryB);
}else {
pizza = new ChengduPizza(factoryB);
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
public static void main(String[] args) {
PizzaStore pizzaStore = new StarBoundPizzaStore();
Pizza pizza = pizzaStore.createPizza("舊成都披薩");
System.out.println(pizza.toString());
Pizza pizza1 = pizzaStore.createPizza("新北京披薩");
System.out.println(pizza1.toString());
}
}
結果:
開始準備 舊成都披薩:
以下原料準備完畢:虎皮青椒,郫縣豆瓣,花椒油,食用鹽
烘烤...
切片...
包裝...
披薩製作完畢:舊成都披薩
生產原料:虎皮青椒 郫縣豆瓣 花椒油 食用鹽
開始準備 新北京披薩:
以下原料準備完畢:虎皮青椒,老乾媽豆瓣醬,花椒油,食用鹽
烘烤...
切片...
包裝...
披薩製作完畢:新北京披薩
生產原料:虎皮青椒 老乾媽豆瓣醬 花椒油 食用鹽