伍迷家园

让编程融入生活
随笔 - 81, 文章 - 0, 评论 - 1422, 引用 - 136
数据加载中……

小菜编程成长记(九 反射——程序员的快乐!)

(续上篇)
         “到底如何去改良策略模式呢?”小菜恳切地问道。
         “你仔细观察过没有,你的代码,不管是用工厂模式写的,还是用策略模式写的,那个分支的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列表框中,我现在还需要输出到打印机打印成交易单据,我还希望这些清单能存入数据库中,你需要改客户端的代码吗?”
       “这个,你这是加需求了,更改当然是必须的。”
       “更改是必须的没有错,但为什么我只是要对交易清单加打印和存数据,就需要去改客户端的代码呢?这两者没什么关系吧?”大鸟说。
       “啊,你的意思是…………”
        “别急着下结论,先去好好思考一下再说。”大鸟打断了小菜。

(待续)
本文源代码

 

posted on 2007-03-22 16:51 伍迷 阅读(6592) 评论(65)  编辑 收藏 所属分类: 面向对象小菜编程成长记

评论

#1楼    回复  引用    

有理!受益匪浅啊!期待中!不过这次更新得真快啊!刚刚还在想上篇看得真不过瘾!然后就咦!又是一篇!:)
2007-03-22 17:04 | 平淡生活 [未注册用户]

#2楼    回复  引用    

不错。。学习了。。
2007-03-22 17:04 | teana [未注册用户]

#3楼 [楼主]   回复  引用  查看    

@平淡生活
@teana
最近相对空一些,就多写点,之前一停就是半年,太懒了。
2007-03-22 17:12 | 伍迷      

#4楼    回复  引用  查看    

简单易懂,真强,学习ing
2007-03-22 17:13 | Ame      

#5楼    回复  引用    

好文章!等待下一篇的出来!
2007-03-22 17:43 | 菜鸟学习 [未注册用户]

#6楼 [楼主]   回复  引用  查看    

@菜鸟学习
@Ame
谢谢鼓励!不过建议不要只是等待。如果你以前从没有用过反射,最好实际练练,看懂!=学会,学会!=做对。当然,如果你早就理解这个技术,那么就当是看看玩玩。
2007-03-22 17:46 | 伍迷      

#7楼    回复  引用  查看    

haha...寫得非常幽默啊
2007-03-22 18:00 | beal      

#8楼    回复  引用    

厉害,终于弄懂了反射
2007-03-22 18:26 | join [未注册用户]

#9楼    回复  引用    

很好,整个系列写完之后可以出书了,保证受大学生的广泛欢迎!
2007-03-22 18:32 | lizhizhe2000 [未注册用户]

#10楼    回复  引用    

不错 学习了 希望博主能再接再厉多写点
看了很多书都没有看你的文章明白的快
2007-03-22 20:18 | Apple [未注册用户]

#11楼 [楼主]   回复  引用  查看    

@lizhizhe2000
@Apple
@beal
@join
哈,我的这种文章上不了台面的。前几天,一位杂志社的朋友找到我,跟我约稿,我说我比较擅长写《小菜编程成长记》这种风格的技术文章,他看了后说他们杂志不适合这种风格,希望我改写类似大学论文式的文章,我费了老大力,写了内容和七八九这三段差不多的东东,去掉了当中的调侃,给他看后,他很抱歉的对我说,不能采用,原因就是语言不够专业,内容没有深度。我就不太懂了,难道非要写得没几人看得懂的才叫好文章,才可以发表在杂志上吗?(此乃气话,有深度又易懂的文章其实很多)不过呢,写论文式的东西我还真不擅长,我也不知道如何才能把大道理讲明白,不专业就不专业吧,博客是自己的天地,我写起来也没太大压力,不要再去投稿丢脸了。

谢谢你们鼓励,尽管过于夸张,不过伍迷还是很受用。在这里回复的朋友对我写作的肯定,也算是给我前几天被退稿而不爽的一种激励吧。我会坚持写下去,尽管它是那么的不专业。
2007-03-22 20:26 | 伍迷      

#12楼    回复  引用  查看    

一句话:谢谢作者。

继续关注ing....
2007-03-22 22:11 | 萧林      

#13楼    回复  引用    

现在的大学就却这种通俗易懂,又有内容的东西
2007-03-22 22:30 | char [未注册用户]

#14楼    回复  引用  查看    

辛苦!期待下文中...
2007-03-23 08:15 | 小哈      

#15楼    回复  引用  查看    

这种学习的方式真的很神奇
尽管每个人都能想到
但不是每个人都能做到
或许可以把系列文章归档出书
说不定会收到追捧
呵呵
2007-03-23 09:52 | 碳碳      

#16楼    回复  引用    

加油!!!

我们会继续关注的!
2007-03-23 11:07 | 天客 [未注册用户]

#17楼    回复  引用    

写的不错,有意思,易明白!
2007-03-23 11:41 | 扬帆天下 [未注册用户]

#18楼    回复  引用    

真实太容易理解了,而且看完印象深刻,继续努力,期待下文。。。
支持作者
2007-03-23 13:17 | yufengly [未注册用户]

#19楼    回复  引用    

前几篇都有点像是介绍性的东西,这篇可是真正学到点东西哈
2007-03-23 13:19 | william [未注册用户]

#20楼    回复  引用  查看    

终于把反射弄明白了,多谢多谢。

2007-03-23 13:30 | 金色海洋(jyk)      

#21楼    回复  引用  查看    

我是习惯使用面向数据库的方法,如果我来做这个的话,我会这么写。

把变化放在数据库里面。

建立一个表,加上三个字段,打折、满几百、送几百。

打折字段:十则是1,九折是0.9,......
满几百:300
送几百:100

然后再用

打折后金额 = 商品定价 * 打折字段
赠送的金额 = ((打折后金额 ) / 满几百 ) * 送几百

最后的价钱 = 打折后金额 - 赠送的金额

这里类的公式来计算。

2007-03-23 13:37 | 金色海洋(jyk)      

#22楼    回复  引用  查看    

没有其他的意思,我只是说一下我的想法。

你的这个系列确实帮了我很大的忙,以前对工厂、映射迷迷糊糊的,现在我都明白了。

多谢多谢!
2007-03-23 13:39 | 金色海洋(jyk)      

#23楼    回复  引用    

算法不够灵活,只支持一种算法。要么打折要么满多少送多少。。。。。
至少应该支持现有算法的混合运算,比如说打折后再满多少送多少之类的。就是说要对结果按顺序进行处理。
实现这个功能至少有两种方法(因为我想到了两种~):
1、使用递归:
将策略与基础算法分开,策略是一个可以递归的类,策略类包含一个算法。
2、使用数组:
将策略与基础算法分开,策略包含一个算法数组的属性。
Xml保存算法的顺序,不只是单个的算法。至于用什么样的XML格式表示这个算法的顺序方法也有很多种。
这只是我的想法,没有验证,真正写起来可能会有些问题吧,不过原理应该不会错~有兴趣的可以试一下~我自己就不试了。。。。。。
2007-03-23 16:30 | 疯 [未注册用户]

#24楼 [楼主]   回复  引用  查看    

@金色海洋(jyk)
你好,数据库的处理方法可以是国内最常见的用法,包括我自己以前也大部分是这样做的。
首先需要明白你的计算是在数据库中进行,还是程序语言C#中进行。如果在C#中进行,那就和我的方法没什么冲突。用策略是比较好的方法。

但如果是在数据库中进行,那么由于把变化放在数据库中,这就造成需求变化与数据库强耦合了。

比如上楼的“疯”先生提出的,算法的混合运算,先打折,再满几送几,那么是一定要改存储过程了,然后我再要求总计后,再满几送几,你如何办?你会发现,有重复算法的存储过程出现了。如果我需要满100则积10点,您是否打算要再在数据库中加两个字段?

设计模式的原则:对扩展开发,对修改关闭。如果我们写好的东西却需要时常的修改,那就是有问题的。从这个角度讲,存储过程其实并不是好的解决方案。
2007-03-23 19:48 | 伍迷      

#25楼 [楼主]   回复  引用  查看    

@疯
你的考虑很好,如果需要混合运算,那么是需要考虑一些改变。

不过任何设计都是要考虑需求的实际情况的,至少本例是不需要考虑递归或数组。为什么呢,因为商场最多就是对商品先打折,再满几送几,然后再总计的时候再满几送几等,也就是说,这里的变化是可以预期的,我完全可以写定三次下拉的选择,缺省都是正常收费,就可以了。
2007-03-23 19:53 | 伍迷      

#26楼    回复  引用  查看    


一开始我想到的是不修改为目标,这个目标似乎高了点。

只是将修改局部化,我倒有个想法,完全可以在列表中枚举可用的算法。然后引发相关的处理事件。 将程序分为两个部分,一个是界面,一个是实现内部算法的部分,我们只要修改实现内部算法的部分,增加可枚举的项和相关的算法,那么就不需要所谓的xml之类的东西,因为那个东西应该属于实现内部算法的部分,我们放到界面那里,然后修改算法又要修改对应的这部分,实际上是造作的,表面上是很松,实际上xml的内容是需要根据算法来相应变化,是联动的关系。因此还不如在算法部分实现一个可以枚举的接口。

2007-03-23 22:59 | 航天奇侠      

#27楼    回复  引用  查看    

感谢回复。
请注意我的算法,我的算法包括先打折然后在满几百送几百的情况,一个算法适应三种情况。

我在明确一下我的算法,有个地方没有说清楚。

=====

把变化放在数据库里面。

建立一个表,加上三个字段,打折、满几百、送几百。

>>字段里面记录的信息
打折字段:十则是1,九折是0.9,...... //存放“1”的话说明不打折,可能满几百送几百
满几百:300
送几百:100 // 存放“0”的话说明满多少都不送,可能打折。

>>然后在程序里面提取字段的值保留在变量里面


double 打折字段 = ... //从数据库里取值
double 满几百 = ... //从数据库里取值
double 送几百 = ... //从数据库里取值 。这里只是说明一下是从数据库里取值的。

>>在代码中实现下列算法:

打折后金额 = 商品定价 * 打折字段 //if (打折字段 == 1) 不打折
赠送的金额 = ((打折后金额 ) / 满几百 ) * 送几百 //if (送几百 == 0 ) 慢多少都不送。

最后的价钱 = 打折后金额 - 赠送的金额

这里类的公式来计算。

==============

这样可以说明白了吧。

这有一个大的缺点,如果再加入一种方式,那么需要重新考虑算法。运气好的话可以直接加上;运气不好的话就要麻烦了。

当然也可以简单处理,再加一个“策略”。(又回去了)。

可见“策略”是一个简单的,分散算法的一个方式。就是改一个其他的不受影响。





2007-03-24 13:30 | 金色海洋(jyk)      

#28楼    回复  引用  查看    

满几送几 都是在总计之后再送吧,单个商品好像不会满几送几。


>>字段里面记录的信息
打折字段:十则是1,九折是0.9,...... //存放“1”的话说明不打折,可能满几百送几百
满几百:300
送几百:100 // 存放“0”的话说明满多少都不送,可能打折。


如果上面的信息不想放在数据库里的话,放在XML文件里面也行。
2007-03-24 13:32 | 金色海洋(jyk)      

#29楼    回复  引用  查看    

不对呀,我的方法不就是用了一个算法实现了三个要求吗?

用策略的原因是要根据不同的情况调用不同的类,现在好了只有一个类了,那么是不是就暂时不需要策略了呢?
2007-03-24 13:45 | 金色海洋(jyk)      

#30楼 [楼主]   回复  引用  查看    

@金色海洋(jyk)
把配置文件读取改成数据库读取是没有问题的。你的写法我也表示赞同,只不过需要处理好应对变化的问题。

我建议您把我的代码下载后,进行您的想法的改良,如果要用数据库可以用Access,写好后再与我联系,我们共同讨论。

设计模式的应用主要是为了应对变化,如果需求不会变,那么不用设计模式也不会是什么问题,程序可以运行就可以了。这点需要明确。好好加油吧。
2007-03-24 22:39 | 伍迷      

#31楼 [楼主]   回复  引用  查看    

@航天奇侠
你已经提到了我打算后期要讲的一个问题,就是界面和业务逻辑的如何解耦,目前的代码的确还是不够漂亮。(我更愿意把编程当成艺术而不是技术,所以用漂亮来形容),请把我的代码改良吧,或许我们最后想到一处去了。
2007-03-24 22:44 | 伍迷      

#32楼    回复  引用  查看    

你的方法是作了三个类,每个类对应一种算法,通过反射的方式来判断用哪一个;
而我的是用一个类来实现三种算法,至于判断吗,选哪个下拉列表框就是那种算法。可以说是“自动”识别吧。代码就是那些,我就不再写完整的代码了。

我也写了一个系列,主要是想说一下我写网站的方法,还想仿造您的方法来写呢,不过估计不会有你的文章那么幽默形象。
2007-03-26 15:37 | 金色海洋(jyk)      

#33楼 [楼主]   回复  引用  查看    

@金色海洋(jyk)
其实把想法写出来,受益最大的是自己,鼓励你多写些。
2007-03-27 13:52 | 伍迷      

#34楼    回复  引用  查看    

我也想多写,只是有时候写不好的话会挨骂。会被骂得很惨。
不过好在这里可以修改,也可以删除回复。
不像csdn,什么都改不了。
我还是多看看书,然后再写吧,少唉点吗,呵呵。
2007-03-28 19:17 | 金色海洋(jyk)      

#35楼    回复  引用  查看    

感激,让我这个菜鸟顿悟
这样的写法太好了
如果大大你出书,我肯定购买
2007-03-29 17:29 | 沉默天蝎       

#36楼    回复  引用    

强,很受益啊,感谢楼主,写这么好的文章出来
2007-05-11 10:55 | hanlei [未注册用户]

#37楼    回复  引用    

强 学习!感谢!

#38楼    回复  引用    

楼主,//将读取到的记录绑定到下拉列表框中
好像没生效。我在comboBox1下拉框中没看到有填充。click事件提示:/根据用户的选项,查询用户选择项的相关行
DataRow dr = ((DataRow[])ds.Tables[0].Select("name='" + comboBox1.SelectedItem.ToString() + "'"))[0]; 有问题。
2007-11-27 19:57 | liuhongwei [未注册用户]

#39楼 [楼主]   回复  引用  查看    

@liuhongwei
我在本文后面有源代码下载,您可以下载后试试看,代码在VS2005下调试通过的。
2007-11-27 20:25 | 伍迷      

#40楼    回复  引用  查看    

这是相间恨晚啊
2007-12-03 12:13 | 黑星      

#41楼    回复  引用    

这一系列文章真的不错,很不错的文章,非常喜欢这样闲侃式的把思想传授给我们。

反射的技术在VC中的应用应该与CObject继承下来的类的永久支持类似,不过MFC在内部是通过switch语句和宏定义语句来实现永久支持的,这边C#是内部即支持的,C#更加方便与易于理解,不知道这样的理解对不对,没有用过C#?

如果没有错,晚上在VC中使用MFC改写,希望更多的朋友能够理解这些东西……

感谢博主,希望继续出好文章……
2007-12-04 13:46 | newrain [未注册用户]

#42楼    回复  引用    

在VC的MFC中构造时突然发现,有好些东西需要修改才能支持反射技术,主要要CRuntimeClass类中的Load函数与CreateObject函数,这两个函数一个是不带参数的,另一个创建的类是空的构造函数创建的,因此要修改发现实在是很困难,而且需要修改很多内在的东西,这也就走进维护的噩梦了,因此失去了这个题目的意义。下面把大致代码贴出来,希望有高手能够进行更好的实现,呵呵,记得告诉我哦……

需要在CRuntimeClass类中支持
CCashSuper *CRuntimeClass::CreateObject(CString &strArguments)
CRuntimeClass::Load(CString &strClassName)

实现为:

CCashSuper *CRuntimeClass::CreateObject(CString &strArguments)
{
if(this->m_pfnCreateObject == NULL)
return NULL;
CCashSuper *pCCashSuperRet=NULL;
pCCashSuperRet =(CCashSuper *) this->m_pfnCreateObject(); // 返回的是CObject对象,因此需要强制转换一下
// 此处添加参数的调用,但是此处添加已经失去了构造的意义,且
//每一个类中需要添加一个空的构造函数,代码维护将产生困难
return pCCashSuperRet;
}

CRuntimeClass *PASCAL CRuntimeClass::Load(CString &strClassName)
{
CRuntimeClass *pRet=NULL;
//pBaseClass是宏定义中的值,为CRuntimeClass的常量值
for(pRet=CRuntimeClass::m_pBaseClass; pRet!=NULL; pRet=pRet->m_pNextClass)
{
if(pRet->m_lpszClassName == strClassName )
break;
}
return pRet;

}

// 现金收取父类
class CCashSuper : public CObject
{
// 抽象方法:收取现金,参数为原价,返回为当前价
public:
virtual double acceptCash(double money) //本来是作为纯虚拟函数实现的,但动态创建不能够实现
// 如果修改成纯虚拟函数,那么下面CCashNormal的动态创建
// 的baseclass名称需要CObject,这样会使代码更难懂一些,因此
// 不设置成纯虚拟函数。
{ }
DECLARE_DYNCREATE(CCashSuper)
};

IMPLEMENT_DYNCREATE(CCashSuper,CObject)

// 正常收费类,继承CCashSuper类
class CCashNormal : public CCashSuper
{
public:
virtual double acceptCash(double money)
{
return money;
}

DECLARE_DYNCREATE(CCashNormal)
};

IMPLEMENT_DYNCREATE(CCashNormal,CCashSuper)

// 返回折扣,继承CCashSuper类
class CCashReturn : public CCashSuper
{
public:
// 初始化时,必须要输入返利条件和返利数目,比如满300返100,则
// m_dmoenyCondition为300,m_dmoneyReturn为100
CCashReturn(double moneyCondition, double moneyReturn)
{
m_dmoneyCondition = moneyCondition;
m_dmoneyReturn = moneyReturn;
}

public:
virtual double acceptCash(double money)
{
double result = money;
// 若大于返利条件,则需要减去返利值
if( money >= m_dmoneyCondition )
result = money - money/m_dmoneyCondition*m_dmoneyReturn;

return result;
}
DECLARE_DYNCREATE(CCashReturn)

private:
double m_dmoneyCondition;
double m_dmoneyReturn;
};

IMPLEMENT_DYNCREATE(CCashReturn,CCashSuper)

// 打折收费,继承CCashSuper
class CCashRebate : public CCashSuper
{
public:
// 初始化时,必须要输入折扣率,如八折就是0.8
CCashRebate(double rebate)
{
m_drebate = rebate;
}

public:
virtual double acceptCash(double money)
{
return money*m_drebate;
}

DECLARE_DYNCREATE(CCashRebate)
private:
double m_drebate;
};

IMPLEMENT_DYNCREATE(CCashRebate,CCashSuper)

/// 策略实施类
class CCashContext
{

private:
CCashSuper *cs;

public:

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

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

然后在主函数中创建相关的界面,即使在console中,也可以通过输入的名字来创建相应的类。

但整个框架由于VC的MFC反射技术实现上的问题,需要修改的地方太多,因此实现的不是很好!
2007-12-04 21:07 | newrain [未注册用户]

#43楼    回复  引用    

很少有人能把设计模式,介绍的清楚易懂,你做到了,而且很好,很试合我们中国人的口味,呵呵,可能说的有点大,不过很适合我,请继续努力,我会经常来光顾的

2007-12-10 21:44 | 小侯 [未注册用户]

#44楼    回复  引用    

大话设计模式,偶在淘宝上订了一本,好好学习一下(dangdang不支持民生卡吗?达人求解)

功能性的东西没什么意思,喜欢讲模式的,现在存在过度应用的问题,谢谢

#45楼    回复  引用  查看    

很強大,很邪惡
2008-04-02 16:15 | OceanKIng      

#46楼    回复  引用    

偶刚刚学习设计模式,表反射的原理一知半解,参照楼主的例子试验,总是报找不到程序集,只好照网上的示例,编译成DLL文件再调用。
在这里弱弱的问一下,请问楼主,反射只能用编译的DLL文件么。
2008-04-28 16:23 | EMP [未注册用户]

#47楼 [楼主]   回复  引用  查看    

@EMP
当然不是,建议再仔细研究,先照着例子做,然后再自己实践.
2008-04-28 16:41 | 伍迷      

#48楼    回复  引用  查看    

很好~~hoho
2008-04-28 16:52 | beyondme      

#49楼    回复  引用    

@伍迷
谢谢楼主
没想到你这么快就回复了,效率也太快了
2008-04-28 16:57 | EMP [未注册用户]

#50楼    回复  引用    

顶!……
2008-05-23 10:14 | 念时回复 [未注册用户]

#51楼    回复  引用    

请问楼主:
我在Asp.net中
自建的类,即存放在App_Code中的例如 Class SimplyFactory
不知如何获得程序集名称,后来上网查了下,说用下面的代码可得到程序集名称
Assembly assembly = System.Reflection.Assembly.Load "mscorlib.dll");
string str_PromramSetName = assembly.GetName().ToString();

可是还是在下面的代码中试验不成功。请问是哪里错了(不知上面的代码获取程序集名称的方式对吗)。谢谢!
Class SimplyFactory

{
Parent parent= null;
Assembly assembly =System.Reflection.Assembly.Load "mscorlib.dll");
string str_PromramSetName = assembly.GetName().ToString();

parent = (Parent)Assembly.Load(str_PromramSetName).CreateInstance(str_PromramSetName+".子类名称字符")

}
2008-06-24 17:36 | siso [未注册用户]

#52楼 [楼主]   回复  引用  查看    

@siso
通常反射更用于一个解决方案中的多个项目的软件系统中,因此不同的项目,当然就有不同的程序集。建议你去研究一下PetShop4的代码,就会理解了。
2008-06-26 21:21 | 伍迷      

#53楼    回复  引用  查看    

楼主帮帮忙,我使用反射不快乐啊 :)
如果把CashRebate的构造函数从string rebate修改改double rebate
反射就会出错 未找到类型“商场管理软件.CashRebate”上的构造函数。
而string会正常运行,就是为何呢?
2008-08-21 21:30 | 阿鹏      

#54楼 [楼主]   回复  引用  查看    

@阿鹏
当然得是string,配置文件读进来的当然是字符串了.
2008-08-22 09:16 | 伍迷      

#55楼    回复  引用  查看    

@伍迷
呵,谢谢回复
不过args不是object[]类型吗
2008-08-22 09:29 | 阿鹏