读《大话设计模式》——应用三层架构的"商场收银系统"

面向对象的四个好处,是可维护、可扩展、可复用和灵活性好。PC电脑里的易插拨,不管硬件哪一个出问题,都可以不影响别的部件的前提下进行修改或替换<----->面向对象的强内聚,松耦合。

设计模式的几大原则:

单一职责原则,就是指就一个类而言,应该仅有一个引起它变化的原因。

开放—封闭原则是说对扩展开发,对修改关闭,通俗的讲,就是我们在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,换句话说就是,应当可以在不必修改源代码的情况下改变这个模块的行为。

依赖倒转原则,原话解释是抽象不应该依赖细节,细节应该依赖于抽象,这话绕口,说白了,就是要针对接口编程。

‘ 迪米特法则(LoD )’ 也叫最少知识原则,简单的 也叫最少知识原则,简单的说,就是如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
注:

迪米特法则还是在讲如何减少耦合的问题,类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。也就是说,信息的隐藏促进了软件的复用。

 

所谓的三层开发,就是关于表现层、业务逻辑层和数据访问层的开发。当然,这其实只是大方向的分层,每个层中都有可能再细分为多个层次和结构。

 

 

 

 门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的门面 门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的门面(Facade)对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。

 

门面(Facade)角色:客户端可以调用这个角色的方法。此角色知晓相关的(一个或者多个)子系统的功能和责任。在正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去。
子系统(subsystem)角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。每一个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。

 

 

 

上图为有无门面角色的对比,“新闻发言人”是门面角色。

 

以下三层架构项目结构图

 

 

 

 

 附上代码

  DAL层

  

namespace ExtendWinformOfThreeTierArchitecture.DAL
{
    public class CashAcceptType
    {
        public DataSet GetCashAcceptType()
        {
        //读取配置文件到DataSet(目前是读配置文件,以后可以很容易的修改为访问数据库)
        DataSet ds = new DataSet();
        ds.ReadXml("CashAcceptType.xml");
        return ds;
        }
    }
}

 

   BLL层

 

namespace ExtendWinformOfThreeTierArchitecture.BLL
{
    //现金收取父类
    abstract class CashSuper
    {
        //抽象方法:收取现金,参数为原价,返回为当前价
        public abstract double acceptCash(double money);
    }
}
View Code
namespace ExtendWinformOfThreeTierArchitecture.BLL
{
    //返利收费
    class CashReturn:CashSuper
    {
        private double moneyCondition = 0.0d;
        private double moneyReturn = 0.0d;
        //初始化时必须要输入返利条件和返利值,比如满300返100
        //则moneyCondition为300,moneyReturn为100
        public CashReturn(string moneyCondition, string moneyReturn)
        {
            this.moneyCondition =double.Parse(moneyCondition);
            this.moneyReturn = double.Parse(moneyReturn);
        }

        public override double acceptCash(double money)
        {
            double result = money;
            //若大于返利条件,则需要减去返利值
            if (money >= moneyCondition)
            {
                result = money - Math.Floor(money / moneyCondition) * moneyReturn;
            }
            return result;
        }
    }
}
View Code
namespace ExtendWinformOfThreeTierArchitecture.BLL
{
    //打折收费消费,继承CashSuper
    class CashRebate:CashSuper
    {
        private double moneyRebate = 1d;
        //初始化时,必需要输入折扣率,如八折,就是0,8
        public CashRebate(string moneyRebate)
        {
            //界面向类传值
            this.moneyRebate = double.Parse(moneyRebate);
        }
        public override double acceptCash(double money)
        {
            return money * moneyRebate;
        }
    }
}
View Code
//正常消费,继承CashSuper
    class CashNormal:CashSuper
    {
        public override double acceptCash(double money)
        {
            return money;
        }
    }
View Code
namespace ExtendWinformOfThreeTierArchitecture.BLL
{
    class CashContext
    {
        //声明一个现金收费父类对象
        private CashSuper cs;

        //设置策略行为,参数为具体的现金收费子类(正常,打折或返利)
        public void setBehavior(CashSuper csuper)
        {
            this.cs = csuper;
        }

        //得到现金促销计算结果(利用了多态机制,不同的策略行为导致不同的结果)
        public double GetResult(double money)
        {
            return cs.acceptCash(money);
        }
    }
}
View Code

  门面模式类

 1 //add
 2 using ExtendWinformOfThreeTierArchitecture.DAL;
 3 using System.Data;
 4 using System.Reflection;
 5 
 6 namespace ExtendWinformOfThreeTierArchitecture.BLL
 7 {
 8     public class CashFacade
 9     {
10         const string ASSEMBLY_NAME = "ExtendWinformOfThreeTierArchitecture.BLL";
11 
12         //得到现金收取类型列表,返回字符串数组
13         public string[] GetCashAccepTypetList()
14         {
15             CashAcceptType cat = new CashAcceptType();
16             DataSet ds = cat.GetCashAcceptType();
17             int rowCount = ds.Tables[0].DefaultView.Count;
18             string[] arrarResult = new string[rowCount];
19 
20             for (int i = 0; i < rowCount; i++)
21             {
22                 arrarResult[i] = (string)ds.Tables[0].DefaultView[i]["name"];
23             }
24             return arrarResult;
25         }
26         
27         /// <summary>
28         /// 用于根据商品活动的不同和原价格,计算此商品的实际收费
29         /// </summary>
30         /// <param name="selectValue">下拉列表选择的折价类型</param>
31         /// <param name="startTotal">原价</param>
32         /// <returns>实际价格</returns>
33         public double GetFactTotal(string selectValue, double startTotal)
34         {
35             CashAcceptType cat = new CashAcceptType();
36             DataSet ds = cat.GetCashAcceptType();
37 
38             CashContext cc = new CashContext();
39             //根据用户的选项,查询用户选择项的相关行
40             DataRow dr = ((DataRow[])ds.Tables[0].Select("name='" + selectValue+ "'"))[0];
41             //声明一个参数的对象数组
42             object[] args = null;
43             //若有参数,则将其分割成字符串数组,用于实例化时所用的参数
44             if (dr["para"].ToString() != "")
45             {
46                 args = dr["para"].ToString().Split(',');
47             }
48             //通过反射实例化出相应的算法对象
49             cc.setBehavior((CashSuper)Assembly.Load(ASSEMBLY_NAME).
50                 CreateInstance(ASSEMBLY_NAME+"." + dr["class"].ToString(), false,
51                 BindingFlags.Default, null, args, null, null));
52             return cc.GetResult(startTotal);
53         }
54     }
55 }

  UI层

  

 1 //add
 2 using CCWin;
 3 using ExtendWinformOfThreeTierArchitecture.BLL;
 4 
 5 namespace ExtendWinformOfThreeTierArchitecture
 6 {
 7   
 8     public partial class frmMain :Skin_Metro
 9     {
10 
11         public frmMain()
12         {
13             InitializeComponent();
14         }
15         
16         //声明一个double变量total来计算总计
17         double total = 0.0d;
18         CashFacade cf = new CashFacade();
19 
20         private void btnConfirm_Click(object sender, EventArgs e)
21         {
22             //声明一个double变量totalPrices
23             double totalPrices = 0d;
24             //传进下拉选择框的值和原价,计算实际收费结果
25             totalPrices = cf.GetFactTotal(
26                 cbxType.SelectedItem.ToString(),
27                 Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));
28             //将每个商品合计计入总计
29             total = total + totalPrices;
30             //在列表框中显示信息
31             lbxList.Items.Add("单价:" + txtPrice.Text + "  数量:" + txtNum.Text + "  合计:" + totalPrices.ToString());
32             //在lblTotalShow标签上显示总计数
33             lblTotalShow.Text = total.ToString();
34         }
35 
36         private void btnReset_Click(object sender, EventArgs e)
37         {
38             total = 0.0;
39             txtPrice.Text = "";
40             txtNum.Text = "";
41             lblTotalShow.Text = "";
42             lbxList.Items.Clear();
43             cbxType.SelectedIndex = 0;
44         }
45 
46         private void txtNum_KeyPress(object sender, KeyPressEventArgs e)
47         {
48             //数字0~9所对应的keychar为48~57
49             e.Handled = true;
50             //输入0-9
51             if ((e.KeyChar >= 47 && e.KeyChar <= 58) || e.KeyChar == 8)
52             {
53                 e.Handled = false;
54             } 
55         }
56 
57         private void txtPrice_KeyPress(object sender, KeyPressEventArgs e)
58         {
59             //数字0~9所对应的keychar为48~57
60             e.Handled = true;
61             //输入0-9
62             if ((e.KeyChar >= 47 && e.KeyChar <= 58) || (e.KeyChar == 8 || e.KeyChar==46))
63             {
64                 e.Handled = false;
65             } 
66         }
67 
68         private void frmMain_Load(object sender, EventArgs e)
69         {
70             //读取下拉列表框的值
71             cbxType.DataSource = cf.GetCashAccepTypetList();
72 
73             //要下拉选择框在加载的时候,就选择索引为0的元素"正常消费"
74             cbxType.SelectedIndex = 0;
75         }
76     }
77 }

 

温馨提示,xml文件——CashAcceptType.cs应该放在如下图所示的位置

   

 

 

现在用了门面模式以后,耦合比以前要少很多了,更改会更加方便,扩展也很容易了

posted on 2020-07-12 21:26  笠侹凯树  阅读(342)  评论(0编辑  收藏  举报

导航