【大話設計模式】——簡單工廠模式

ZeroWM發表於2014-05-22

一、概念

  簡單工廠模式(Simple Factory Pattern)屬於建立型模式,又叫做靜態工廠方法模式(Static FactoryMethod Pattern),但是不屬於23GOF設計模式之一。簡單工廠模式是由一個工廠物件決定建立出哪一種產品類的例項。


二、UML圖

  簡單工廠主要分為三個角色:工廠(Creator)角色、抽象產品(Product)角色、具體產品(Concrete Product)角色。

  工廠角色:該模式核心,它負責建立所有例項的內部邏輯。工廠類可以被外界直接呼叫,建立所需要的產品。

  抽象產品角色:所建立的所有物件的父類,它負責描述所有例項所共有的公共介面。

       具體產品:該模式的建立目標,所有建立的物件都是充當這個角色的某個具體類的例項。


三、例項解析

  以下是我自己想的一個簡單工廠模式場景,如果有不妥的地方還請大家指出。

  我做的是一個簡易的網咖收費系統,CashFactory充當的是工廠的角色,它會告訴我們如何建立白天收費方式和晚上收費方式,需要例項化哪些物件,需要什麼引數;具體產品 白天、晚上 收費方法,通過override,實現了從基類繼承成員的新實現。網咖收費抽象類 為白天和晚上 收費方式的父類,描述了兩者的公共介面。




程式碼如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
    public partial class 簡易網咖收費系統 : Form
    {
        public 簡易網咖收費系統()
        {
            InitializeComponent();
        }

        //窗體啟動的時候載入收費方式,是晚上收費,還是白天收費
       private void Form1_Load(object sender, EventArgs e)
        {
            cbxTimeType.Items.AddRange(new object[] { "白天", "晚上" });//在時間段的選擇框里載入收費選項  白天,晚上
            cbxTimeType.SelectedIndex = 0;//索引從0開始,從下往上依次遞增
        }

        //單擊開始按鈕之後,收費系統開始計時
       private void btnOK_Click(object sender, EventArgs e)
       {
           lbxList.Items.Add(cbxTimeType .Text );
           txtStartTime.Text = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");//開始時間文字框獲取開始時間
           lbxList.Items.Add("開始時間:" + txtStartTime.Text);//同時在下面的列表框中載入開始時間資訊
       }
        
        //單擊停止按鈕後,收費系統停止計時
        private void btnStop_Click(object sender, EventArgs e)
        {
            txtEndTime.Text = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
            lbxList.Items.Add("結束時間:" + txtEndTime.Text);

        }  

       //單擊結賬按鈕,系統開始結賬
        private void btnCharge_Click(object sender, EventArgs e)
        {
            double timeThrough = 0.0d;//定義一個變數timeThrough代表花費的時間(小時)
            DateTime DateTime1, DateTime2;//定義兩個時間變數
            DateTime1 = Convert.ToDateTime(txtStartTime.Text);//把文字框中的時間從字串格式轉換成時間格式
            DateTime2 = Convert.ToDateTime(txtEndTime.Text);

            string DateDiff = null;//定義變數時間差(x天x小時x分鐘x秒)

            TimeSpan ts1 = new TimeSpan(DateTime1.Ticks);
            TimeSpan ts2 = new TimeSpan(DateTime2.Ticks);
            TimeSpan ts = ts1.Subtract(ts2).Duration();
            
            //計算時間差
            DateDiff = ts.Days.ToString() + "天" + ts.Hours.ToString() + "小時" + ts.Minutes.ToString() + "分鐘" + ts.Seconds.ToString() + "秒";
            timeThrough = Convert.ToDouble (ts.Days.ToString ()) * 24 + Convert.ToDouble (ts.Hours.ToString()) + Convert.ToDouble (ts.Minutes.ToString()) / 60 +Convert.ToDouble ( ts.Seconds.ToString() )/ 3600;          
            lbxList.Items.Add("通過時間:" + DateDiff);

            //利用簡單工廠模式根據下拉選擇框,生成相應的物件。
            BarCharge  barCharge = CashFactory.createCashAccept(cbxTimeType.SelectedItem.ToString());
            double money = 0d;
            money = barCharge.acceptCash(Convert.ToDouble(timeThrough) * 2);
            lbxList.Items.Add("需要支付上網的費用:" + money +"元");//通過多型,可以得到收取費用的結果
        }
   
    }


    //建立一個網咖現金收取的抽象類
     abstract class BarCharge
     {
         public abstract double acceptCash(double money);//收取現金,引數為原價,返回為當前價
     }


    //網咖的收費機制分為白天和晚上收費兩種情況

     class Day : BarCharge //白天收費情況
     {
         public override double acceptCash( double money)
         {
             return money  ;//白天收費,返回錢數
         }
     }

     class Night : BarCharge //晚上收費情況
     {
         private double moneyRebate = 1d;
         
         //晚上收費,初始化必須要輸入收費條件,晚上優惠率
         public Night(string moneyRebate)
         {
             this.moneyRebate = double.Parse(moneyRebate);   
         }
         //晚上收費=晚上收費*優惠率
         public override double acceptCash(double money)
         {
             
             return money * moneyRebate ;
         }
     }

    //現金收費工廠類
     class CashFactory
     {
         public static BarCharge createCashAccept(string type)
         {
             BarCharge bc = null;
             switch (type)//根據條件返回白天和晚上收費情況
             {
                 case "白天":
                     bc = new Day();//例項化物件
                     break;
                 case "晚上":
                     bc = new Night("0.8");//給出必要引數
                     break;
             }
             return bc;
         }
     }




}


執行結果:



簡單工廠解決了物件的建立問題,免除了直接建立物件的步驟,實現了責任的分割。

但是由於工廠本身包括了所有的收費方式,每次維護或者擴充套件收費方式都要改動這個工廠,以至於程式碼需要重新編譯部署,這樣違背了開放-封閉原則。


四、感受

  走別人的的路,不如自己 to do (不如的意思是更)。先敲書上的例子,第一遍也許不懂,第二遍就清晰很多,第三遍就覺得自己能寫點東西出來。不要總是覺得自己看不懂,學不好,只有先學了才能更好。沒有之前的8個饅頭,就沒有第9個饅頭的飽(好撐啊~~)。

相關文章