我們來看一個具體需求
看一個披薩的項(xiàng)目仅父,要便于披薩種類的擴(kuò)展,要便于維護(hù)榛斯,有以下要求:
- 披薩的種類很多(比如GreekPizz磺箕、CheesePizz等)
- 披薩的制作有prepare,bake扮宠,cut西乖,box
- 完成披薩店訂購功能
首先我們先編寫一個Pizza
抽象類:
package com.cxc.factory.simplefactory.pizzastore.pizza;
/**
* 抽象Pizza類
*/
public abstract class Pizza {
//Pizza名字
protected String name;
//準(zhǔn)備原材料,不同的披薩不一樣,因此我們做成抽象方法
public abstract void preable();
//切
public void cut(){
System.out.println("cut");
}
//烘烤
public void bake(){
System.out.println("bake");
}
//打包
public void box(){
System.out.println("box");
}
public void setName(String name) {
this.name = name;
}
}
然后編寫不同的種類的披薩類去繼承這個抽象類浴栽,并實(shí)現(xiàn)抽象方法:
package com.cxc.factory.simplefactory.pizzastore.pizza;
public class CheesePizza extends Pizza {
@Override
public void preable() {
System.out.println("給制作奶酪披薩準(zhǔn)備原材料");
}
}
package com.cxc.factory.simplefactory.pizzastore.pizza;
public class GreekPizza extends Pizza {
@Override
public void preable() {
System.out.println("給希臘披薩準(zhǔn)備原材料");
}
}
接下來是完成披薩訂單類荒叼,在這里面根據(jù)輸入的披薩種類的不同來新建不同的披薩對象:
package com.cxc.factory.simplefactory.pizzastore.order;
import com.cxc.factory.simplefactory.pizzastore.pizza.CheesePizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.GreekPizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.Pizza;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class OrderPizza {
public OrderPizza() {
Pizza pizza = null;
String orderType; //訂購披薩的類型
do{
orderType = getType();
if (orderType.equals("greek")){
pizza = new GreekPizza();
}else if (orderType.equals("cheese")){
pizza = new CheesePizza();
}else {
break;
}
pizza.preable();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
private String getType(){
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch (Exception e){ }
return "";
}
}
披薩店訂購:
package com.cxc.factory.simplefactory.pizzastore.order;
/**
* 相當(dāng)于一個客戶端,發(fā)出訂購
*/
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza();
}
}
上面實(shí)現(xiàn)的這個案例中存在問題典鸡,因?yàn)槲覀冃陆ㄅ_的代碼編寫在披薩訂單類里面被廓,所以如果我們需要擴(kuò)展一個新的披薩種類的時候,除了需要新建擴(kuò)展類之外萝玷,還需要在此處改代碼嫁乘,違反了開閉原則。在這個案例中需要用到工廠模式來更改球碉。
一蜓斧、簡單工廠模式
1、基本介紹
- 簡單工廠模式是屬于創(chuàng)建型模式睁冬,是工廠模式的一種挎春。簡單工廠模式是由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例。簡單工廠模式是工廠模式家族中最簡單實(shí)用的模式豆拨。
- 簡單工廠模式:定義了一個創(chuàng)建對象的類直奋,由這個類來封裝實(shí)例化對象的行為。
- 在軟件開發(fā)中施禾,當(dāng)我們會用到大量的創(chuàng)建某種脚线、某類或某批對象時,就會使用到工廠模式弥搞。
那對于上面的案例我們?nèi)绾问褂煤唵喂S方法來更改呢邮绿?
首先我們需要將披薩訂單類OrderPizza
中生產(chǎn)披薩的代碼抽取出來,形成一個簡單工廠類:
package com.cxc.factory.simplefactory.pizzastore.order;
import com.cxc.factory.simplefactory.pizzastore.pizza.CheesePizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.GreekPizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.Pizza;
/**
* 簡單工廠模式
*/
public class SimpleFactory {
/**
* 根據(jù)orderType返回對應(yīng)的Pizza對象
* @param orderType
* @return
*/
public Pizza createPizza(String orderType){
Pizza pizza = null;
System.out.println("簡單工廠模式");
if (orderType.equals("greek")){
pizza = new GreekPizza();
}else if (orderType.equals("cheese")){
pizza = new CheesePizza();
}
return pizza;
}
}
披薩訂單類OrderPizza1
可以更改為如下:
package com.cxc.factory.simplefactory.pizzastore.order;
import com.cxc.factory.simplefactory.pizzastore.pizza.CheesePizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.GreekPizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.Pizza;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class OrderPizza1 {
//定義一個簡單工廠對象
private SimpleFactory simpleFactory;
Pizza pizza = null;
public OrderPizza1(SimpleFactory simpleFactory) {
setFactory(simpleFactory);
}
public void setFactory(SimpleFactory simpleFactory){
String orderType = ""; //用戶輸入的
this.simpleFactory = simpleFactory; //設(shè)置簡單工廠對象
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);
if (pizza != null){
//訂購成功
pizza.preable();
pizza.bake();
pizza.cut();
pizza.box();
}else{
System.out.println("訂購披薩失敗");
break;
}
}while (true);
}
private String getType(){
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch (Exception e){ }
return "";
}
}
訂購客戶端:
package com.cxc.factory.simplefactory.pizzastore.order;
/**
* 相當(dāng)于一個客戶端攀例,發(fā)出訂購
*/
public class PizzaStore {
public static void main(String[] args) {
//使用簡單工廠模式
new OrderPizza1(new SimpleFactory());
}
}
這樣改動后船逮,如果我們需要擴(kuò)展新的披薩種類,只需要在簡單工廠類中更改代碼肛度,其它地方不需要進(jìn)行更改傻唾。
接下來對于這個案例,還有一個新需求如下:
客戶在點(diǎn)披薩時承耿,可以點(diǎn)不同口味的披薩,比如北京的奶酪Pizza伪煤、北京的胡椒Pizza或者是倫敦是奶酪Pizza加袋、倫敦的胡椒Pizza。
這個時候有兩種思路:
- 思路1:使用簡單工廠模式抱既,創(chuàng)建不同的簡單工廠類职烧,比如BJPizzaSimpleFactory、LDPizzaSImpleFactory等,但是考慮到項(xiàng)目的規(guī)模蚀之,以及軟件的可維護(hù)性蝗敢、可擴(kuò)展性這個思路并不是很好。
- 思路2:使用工廠方法模式足删。
二寿谴、工廠方法模式
1、基本介紹
工廠方法模式:定義了一個創(chuàng)建對象的抽象方法失受,由子類決定要實(shí)例化的類讶泰。工廠方法模式將對象的實(shí)例化推遲到子類。
對于上述案例的新需求拂到,我們使用工廠方法模式更改痪署,OrderPizza是一個抽象的工廠類,其中的createPizza()方法為抽象方法兄旬,然后定義了不同地區(qū)口味的披薩工廠類:北京口味披薩工廠類(BJOrderPizza)狼犯,倫敦口味披薩工廠類(LDOrderPizze),這兩個披薩工廠類分別去生產(chǎn)各個地區(qū)的披薩领铐,類圖如下所示:
Pizza類:
package com.cxc.factory.factorymethod.pizzastore.pizza;
/**
* 抽象Pizza類
*/
public abstract class Pizza {
//Pizza名字
protected String name;
//準(zhǔn)備原材料辜王,不同的披薩不一樣,因此我們做成抽象方法
public abstract void preable();
//切
public void cut(){
System.out.println("cut");
}
//烘烤
public void bake(){
System.out.println("bake");
}
//打包
public void box(){
System.out.println("box");
}
public void setName(String name) {
this.name = name;
}
}
BJCheesePizza類:
package com.cxc.factory.factorymethod.pizzastore.pizza;
public class BJCheesePizza extends Pizza {
@Override
public void preable() {
System.out.println(" 北京的奶酪pizza準(zhǔn)備原材料 ");
}
}
BJPepperPizza類:
package com.cxc.factory.factorymethod.pizzastore.pizza;
public class BJPepperPizza extends Pizza {
@Override
public void preable() {
System.out.println(" 北京的胡椒pizza準(zhǔn)備原材料");
}
}
LDCheesePizza類:
package com.cxc.factory.factorymethod.pizzastore.pizza;
public class LDCheesePizza extends Pizza {
@Override
public void preable() {
System.out.println(" 倫敦的奶酪pizza準(zhǔn)備原材料");
}
}
LDPepperPizza類:
package com.cxc.factory.factorymethod.pizzastore.pizza;
public class LDPepperPizza extends Pizza {
@Override
public void preable() {
System.out.println(" 倫敦的胡椒pizza準(zhǔn)備原材料");
}
}
抽象的工廠類OrderPizza:
package com.cxc.factory.factorymethod.pizzastore.order;
import com.cxc.factory.factorymethod.pizzastore.pizza.Pizza;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public abstract class OrderPizza {
/**
* 定義一個抽象的方法
* @param orderType
* @return
*/
public abstract Pizza createPizze(String orderType);
public OrderPizza() {
Pizza pizza = null;
String orderType; //訂購披薩的類型
do{
orderType = getType();
//抽象的方法罐孝,由工廠子類完成
pizza = createPizze(orderType);
pizza.preable();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
private String getType(){
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch (Exception e){ }
return "";
}
}
兩個工廠子類分別去實(shí)現(xiàn)OrderPizza類:
package com.cxc.factory.factorymethod.pizzastore.order;
import com.cxc.factory.factorymethod.pizzastore.pizza.*;
public class LDOrderPizza extends OrderPizza {
@Override
public Pizza createPizze(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")){
pizza = new LDCheesePizza();
}else if (orderType.equals("pepper")){
pizza = new LDPepperPizza();
}
return pizza;
}
}
package com.cxc.factory.factorymethod.pizzastore.order;
import com.cxc.factory.factorymethod.pizzastore.pizza.BJCheesePizza;
import com.cxc.factory.factorymethod.pizzastore.pizza.BJPepperPizza;
import com.cxc.factory.factorymethod.pizzastore.pizza.Pizza;
public class BJOrderPizza extends OrderPizza {
@Override
public Pizza createPizze(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")){
pizza = new BJCheesePizza();
}else if (orderType.equals("pepper")){
pizza = new BJPepperPizza();
}
return pizza;
}
}
披薩訂購:
package com.cxc.factory.factorymethod.pizzastore.order;
public class PizzaStore {
public static void main(String[] args) {
//在此示例創(chuàng)建北京口味的各種披薩
new BJOrderPizza();
}
}
三呐馆、抽象工廠模式
1、基本介紹
- 抽象工廠模式定義了一個接口(interface)用于創(chuàng)建相關(guān)或有依賴關(guān)系的對象簇莲兢,而無需指明具體的類
- 抽象工廠模式可以將簡單工廠模式和工廠方法模式進(jìn)行整合
- 從設(shè)計(jì)層面看汹来,抽象工廠模式就是對簡單工廠模式的改進(jìn)(或者稱為進(jìn)一步的抽象)
- 將工廠抽象成兩層,抽象工廠和具體實(shí)現(xiàn)的工廠子類改艇。程序員可以根據(jù)創(chuàng)建對象類型使用對應(yīng)的工廠子類收班。這樣將單個的簡單工廠類變成了工廠簇,更利于代碼的維護(hù)和擴(kuò)展
上面的披薩案例使用抽象工廠模式后類圖如下所示:
接下來我們再來將其實(shí)現(xiàn)一下谒兄,抽象的工廠接口如下:
package com.cxc.factory.absfactory.pizzastore.order;
import com.cxc.factory.absfactory.pizzastore.pizza.Pizza;
/**
* 抽象工廠模式的抽象層
*/
public interface AbsFactory {
/**
* 由下面的具體子類實(shí)現(xiàn)
* @param orderType
* @return
*/
public Pizza createPizza(String orderType);
}
兩個具體的工廠子類:
package com.cxc.factory.absfactory.pizzastore.order;
import com.cxc.factory.absfactory.pizzastore.pizza.BJCheesePizza;
import com.cxc.factory.absfactory.pizzastore.pizza.BJPepperPizza;
import com.cxc.factory.absfactory.pizzastore.pizza.Pizza;
/**
* 具體的工廠子類
*/
public class BJFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("使用的是抽象工廠模式");
Pizza pizza = null;
if (orderType.equals("cheese")){
pizza = new BJCheesePizza();
}else if (orderType.equals("pepper")){
pizza = new BJPepperPizza();
}
return pizza;
}
}
package com.cxc.factory.absfactory.pizzastore.order;
import com.cxc.factory.absfactory.pizzastore.pizza.*;
public class LDFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("使用的是抽象工廠模式");
Pizza pizza = null;
if (orderType.equals("cheese")){
pizza = new LDCheesePizza();
}else if (orderType.equals("pepper")){
pizza = new LDPepperPizza();
}
return pizza;
}
}
披薩訂單類:
package com.cxc.factory.absfactory.pizzastore.order;
import com.cxc.factory.absfactory.pizzastore.pizza.Pizza;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class OrderPizza {
AbsFactory absFactory;
public OrderPizza(AbsFactory absFactory){
this.setAbsFactory(absFactory);
}
private void setAbsFactory(AbsFactory factory){
Pizza pizza = null;
String orderType = "";//用戶輸入
this.absFactory = factory;
do{
orderType = getType();
pizza = factory.createPizza(orderType);
if (pizza != null){
pizza.preable();
pizza.bake();
pizza.cut();
pizza.box();
}else{
System.out.println("訂購失敗");
break;
}
}while (true);
}
private String getType(){
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch (Exception e){ }
return "";
}
}
披薩商店類:
package com.cxc.factory.absfactory.pizzastore.order;
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza(new BJFactory()); //這里演示北京的工廠子類
}
}
四摔桦、JDK源碼分析之工廠模式
Calendar
是一個簡單工廠模式。
例如下面這個創(chuàng)建Calendar
實(shí)例的方法:
Calendar calendar = Calendar.getInstance();
看getInstance
方法的實(shí)現(xiàn):
/**
* Gets a calendar using the default time zone and locale. The
* <code>Calendar</code> returned is based on the current time
* in the default time zone with the default
* {@link Locale.Category#FORMAT FORMAT} locale.
*
* @return a Calendar.
*/
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}
這里返回的createCalendar
方法點(diǎn)進(jìn)去:
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
可以看到在創(chuàng)建實(shí)例的時候是根據(jù)你傳入的參數(shù)不同而來創(chuàng)建不同的實(shí)例的承疲,這是一個簡單工廠模式邻耕。
五、工廠模式小結(jié)
1燕鸽、工廠模式的意義
將實(shí)例化對象的代碼抽象出來兄世,放到一個類中統(tǒng)一管理和維護(hù),達(dá)到和主項(xiàng)目的依賴關(guān)系的解耦啊研,從而提高項(xiàng)目的擴(kuò)展和維護(hù)性御滩。
2鸥拧、三種工廠模式
- 簡單工廠模式
- 工廠方法模式
- 抽象工廠模式
3、設(shè)計(jì)模式的依賴抽象原則
- 創(chuàng)建對象實(shí)例時削解,不要直接new類富弦,而是把這個new類的動作放在一個工廠的方法中,并返回氛驮。也可說為:變量不要直接持有具體類的引用腕柜。
- 不要讓類繼承具體類,而是繼承抽象類或者實(shí)現(xiàn)interface(接口)柳爽。
- 不要覆蓋基類中已經(jīng)實(shí)現(xiàn)的方法媳握。