首页 > 代码库 > 【大话设计模式】——简单工厂模式

【大话设计模式】——简单工厂模式

一、概念

  简单工厂模式(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个馒头的饱(好撑啊~~)。