zoukankan      html  css  js  c++  java
  • 小菜编程成长记(九 反射——程序员的快乐!)

    (续上篇)
             “到底如何去改良策略模式呢?”小菜恳切地问道。
             “你仔细观察过没有,你的代码,不管是用工厂模式写的,还是用策略模式写的,那个分支的switch依然去不掉。原因在哪里?”大鸟反问道。
              “因为程序里有下拉选择,用户是有选择的,那么程序就必须要根据用户的选择来决定实例化哪一个子类对象。无论是在客户端窗体类编程还是到工厂类里编程,这个switch总是少不掉的。问题主要出在这里。”小菜十分肯定的说。
             “是呀,”大鸟道,“所以我们要考虑的就是可不可以不在程序里写明‘如果是打折就去实例化CashRebate类,如果是返利就去实例化CashReturn类’这样的语句,而是在当用户做了下拉选择后,再根据用户的选择去某个地方找应该要实例化的类是哪一个。这样,我们的switch就可以对它说再见了。”
            “听不太懂哦,什么叫‘去某个地方找应该要实例化的类是哪一个’?’小菜糊涂地说
            “,我要说的就是一种编程方式:依赖注入(Dependency Injection),从字面上不太好理解,我们也不去管它。关键在于如何去用这种方法来解决我们的switch问题。本来依赖注入是需要专门的IoC容器提供,比如spring.net,显然当前这个程序不需要这么麻烦,你只需要再了解一个简单的.net技术‘反射’就可以了。”
            “大鸟,你一下子说出又是‘依赖注入’又是‘反射’这些莫名其妙的名词,我有点晕哦!”小菜有些犯困,“我就想知道,如何向switch说bye-bye!至于那些什么概念我不想了解。”
            “心急讨不了好媳妇!你急什么?”大鸟嘲笑道,“反射技术看起来很玄乎,其实实际用起来不算难。”

           “请看下面的两个样例:

    1//实例化方法一   
    2//原来我们把一个类实例化是这样的
    3Animal animal=new Cat();  //声明一个动物对象,名称叫animal,然后将animal实例化成猫类的对象
    4
    5//实例化方法二
    6//我们还可以用反射的办法得到这个实例
    7using System.Reflection;//先引用System.Reflection
    8//假设当前程序集是AnimalSystem,名称空间也是AnimalSystem
    9Animal animal = (Animal)Assembly.Load("AnimalSystem").CreateInstance("AnimalSystem.Cat");

     其中关键是

    Assembly.Load("程序集名称").CreateInstance("名称空间.类名称")

    那也就是说,我们可以在实例化的时候,再给计算机一个类的名称字符串,来让计算机知道应该实例化哪一个类。”大鸟讲解道。
           “你的意思是,我之前写的‘cc.setBehavior(new CashNormal());’可以改写为‘cc.setBehavior((CashSuper)Assembly.Load("商场管理软件").CreateInstance("商场管理软件.CashNormal")’,不过,这只不过是换了种写法而已,又有什么神奇之处呢?”小菜依然迷茫。
            “分析一下,原来new CashNormal()是什么?是否是写死在程序里的代码,你可以灵活更换吗?”大鸟问。
            “不可以,那还换什么,写什么就是什么了呗。”
            “那你说,在反射中的CreateInstance("商场管理软件.CashNormal"),可以灵活更换‘CashNormal’吗?”大鸟接着问。
            “还不是一样,写死在代码…………等等,哦!!!我明白了。”小菜一下子顿悟过来,,兴奋起来。“因为这里是字符串,可以用变量来处理,也就可以根据需要更换。哦,My God!太妙了!”
           “哈哈,博客园中的有篇博文《四大发明之活字印刷——面向对象思想的胜利》中曾经写过,‘体会到面向对象带来的好处,那种感觉应该就如同是一中国酒鬼第一次喝到了茅台,西洋酒鬼第一次喝到了XO一样,怎个爽字可形容呀。’,你有没有这种感觉了?”
            “嗯,我一下子知道这里的差别主要在原来的实例化是写死在程序里的,而现在用了反射就可以利用字符串来实例化对象,而变量是可以
    更换的。”小菜说道。
            “由于字符串是可以写成变量,而变量的值到底是CashReturn(返利),还是CashRebate(打折),完全可以由谁决定?”大鸟再问。
             “当然是由用户在下拉中选择的选项决定,也就是说,我只要把下拉选项的值改成这些算法子类的名称就好了,是吧?”
            “你说得对,不过还不是最好。因为把comboBox的每个选项value都改为算法子类的名称。以后我们要加子类,你不是还要去改comboBox吗?继续往下想,现在我们的代码对有谁依赖?”
            “对下拉控件comboBox的选项有依赖。”
            “那么怎么办,这个控件的选项可不可以通过别的方式生成。比如利用它的绑定?”
            “你的意思是读数据库?”
            “读数据库当然最好了,其实用不着这么麻烦,我们不是有XML这个东东吗,写个配置文件不就解决了?”
            “哦,我知道你的意思了,让它去读XML的配置文件,来生成这个下拉列表框,然后再根据用户的选择,通过反射实时的实例化出相应的算法对象,最终利用策略模式计算最终的结果。好的好的,我马上去写出来。我现在真有一种不把程序写出来就难受的感觉了。”小菜急切的说。
           “OK,还有一个小细节,你的CashRebate和CashReturn在构造函数中都是有参数的,这需要用到CreateInstance()方法的重载函数,不会用去查帮助吧!”
           “好嘞!你别走哦,等我,不见不散!”小菜向外跑着还叫道。
            大鸟摇头苦笑,嘴里嘟囔着:“这小子,忒急了吧!还不见不散呢,难道真没完没了啦!”

    一个小时后,小菜交出了商场收银程序的第五份作业。

    客户端主要代码:

           using System.Reflection;

           DataSet ds;
    //用于存放配置文件信息
            double total = 0.0d;//用于总计

            
    private void Form1_Load(object sender, EventArgs e)
            
    {
                
    //读配置文件
                ds = new DataSet();
                ds.ReadXml(Application.StartupPath 
    + "\\CashAcceptType.xml");
                
    //将读取到的记录绑定到下拉列表框中
                foreach (DataRowView dr in ds.Tables[0].DefaultView)
                
    {
                    cbxType.Items.Add(dr[
    "name"].ToString());
                }

                cbxType.SelectedIndex 
    = 0;
            }


            
    private void btnOk_Click(object sender, EventArgs e)
            
    {
                CashContext cc 
    = new CashContext();
                
    //根据用户的选项,查询用户选择项的相关行
                DataRow dr = ((DataRow[])ds.Tables[0].Select("name='" + cbxType.SelectedItem.ToString()+"'"))[0];
                
    //声明一个参数的对象数组
                object[] args =null;
                
    //若有参数,则将其分割成字符串数组,用于实例化时所用的参数
                if (dr["para"].ToString() != "")
                    args 
    = dr["para"].ToString().Split(',');
                
    //通过反射实例化出相应的算法对象
                cc.setBehavior((CashSuper)Assembly.Load("商场管理软件").CreateInstance("商场管理软件." + dr["class"].ToString(), false, BindingFlags.Default, null, args, nullnull));
                
                
    double totalPrices = 0d;
                totalPrices 
    = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));
                total 
    = total + totalPrices;
                lbxList.Items.Add(
    "单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " "+cbxType.SelectedItem+ " 合计:" + totalPrices.ToString());
                lblResult.Text 
    = total.ToString();
            }

    配置文件 CashAcceptType.xml 的代码

     1<?xml version="1.0" encoding="utf-8" ?>
     2<CashAcceptType>
     3    <type>
     4        <name>正常收费</name>
     5        <class>CashNormal</class>
     6        <para></para>
     7    </type>
     8    <type>
     9        <name>满300返100</name>
    10        <class>CashReturn</class>
    11        <para>300,100</para>
    12    </type>
    13    <type>
    14        <name>满200返50</name>
    15        <class>CashReturn</class>
    16        <para>200,50</para>
    17    </type>
    18    <type>
    19        <name>打8折</name>
    20        <class>CashRebate</class>
    21        <para>0.8</para>
    22    </type>
    23    <type>
    24        <name>打7折</name>
    25        <class>CashRebate</class>
    26        <para>0.7</para>
    27    </type>
    28</CashAcceptType>

    实现的界面同之前一样(可点击使用)


              “大鸟,我再次搞定了,这会是真的明白了。”小菜说。
             “说说看,你现在的理解!”大鸟问。
             “无论你的需求是什么,我现在连程序都不动,只需要去改改XML文件就全部摆平。比如你如果觉得现在满300送100太多,要改成送80,我只需要去XML文件里改就行,再比如你希望增加新的算法,比如积分返点,那我先写一个返点的算法类继承CashSuper,再去改一下XML文件,对过去的代码依然不动。总之,现在是真的做到了程序易维护,可扩展。”小菜得意地坏笑道,“吼吼!此时商场老板以为要改一天的程序,我几分钟就搞定,一天都可以休息。反射——真是程序员的快乐呀!”
           “在做梦了吧,你当老板是傻瓜,会用反射才是正常水平,不会用的早应该走人了。”大鸟打击了小菜的情绪,“不过呢小菜的确是有长进,不再是小菜鸟了。那你说说看,现在代码还有没有问题。”
            “还有不足?不会吧,我都改5次了,重构到了这个地步,还会有什么问题?”小菜不以为然。
           “知足是可以常乐,但知足如何能进步!你的代码真的没有问题了,比如说,你现在把列表是打印在了listBox列表框中,我现在还需要输出到打印机打印成交易单据,我还希望这些清单能存入数据库中,你需要改客户端的代码吗?”
           “这个,你这是加需求了,更改当然是必须的。”
           “更改是必须的没有错,但为什么我只是要对交易清单加打印和存数据,就需要去改客户端的代码呢?这两者没什么关系吧?”大鸟说。
           “啊,你的意思是…………”
            “别急着下结论,先去好好思考一下再说。”大鸟打断了小菜。

    (待续)
    本文源代码

     

  • 相关阅读:
    129. Sum Root to Leaf Numbers
    113. Path Sum II
    114. Flatten Binary Tree to Linked List
    112. Path Sum
    100. Same Tree
    300. Longest Increasing Subsequence
    72. Edit Distance
    自定义js标签库
    JS 实现Table相同行的单元格自动合并示例代码
    mysql 高版本only_full_group_by 错误
  • 原文地址:https://www.cnblogs.com/cj723/p/683951.html
Copyright © 2011-2022 走看看