伍迷家园

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

小菜编程成长记(五 体会简单工厂模式的美妙)

 

(续上篇) 
       次日,小菜再来找大鸟,问道:“你昨天说计算器这样的小程序还可以用到面向对象三大特性?继承和多态怎么可能用得上,我实在不可理解。”
        大鸟:“小菜很有钻研精神吗?好,今天我让你功力加深一级。你先要考虑一下,你昨天写的这个代码,能否做到很灵活的可修改和扩展呢?”
        小菜:“我已经把业务和界面分离了呀,这不是很灵活了吗?”
        大鸟:“那我问你,现在如果我希望增加一个开根(sqrt)运算,你如何改?”
        小菜:“那只需要改Operation类就行了,在switch中加一个分支就行了。”
        大鸟:“问题是你要加一个平方根运算,却需要把加减乘除的运算都得来参与编译,如果你一不小心,把加法运算改成了减法,这不是大大的糟糕。打个比方,如果现在公司要求你为公司的薪资管理系统做维护,原来只有技术人员(月薪),市场销售人员(底薪+提成),经理(年薪+股份)三种运算算法,现在要增加兼职工作人员的(时薪)算法,但按照你昨天的程序写法,公司就必须要把包含有的原三种算法的运算类给你,让你修改,你如果心中小算盘一打,‘TMD,公司给我的工资这么低,我真是郁闷,这会有机会了’,于是你除了增加了兼职算法以外,在技术人员(月薪)算法中写了一句 

if (员工是小菜)
{
    salary 
= salary * 1.1;
}

那就意味着,你的月薪每月都会增加10%(小心被抓去坐牢),本来是让你加一个功能,却使得原有的运行良好的功能代码产生了变化,这个风险太大了。你明白了吗?”
        小菜:“哦,你的意思是,我应该把加减乘除等运算分离,修改其中一个不影响另外的几个,增加运算算法也不影响其它代码,是这样吗?”
        大鸟:“自己想去吧,如何用继承和多态,你应该有感觉了。”
        小菜:“OK,我马上去写。”

    /// <summary>
    
/// 运算类
    
/// </summary>

    class Operation
    
{
        
private double _numberA = 0;
        
private double _numberB = 0;
        
        
/// <summary>
        
/// 数字A
        
/// </summary>

        public double NumberA
        
{
            
getreturn _numberA; }
            
set{ _numberA = value;}
        }


        
/// <summary>
        
/// 数字B
        
/// </summary>

        public double NumberB
        
{
            
getreturn _numberB; }
            
set{ _numberB = value; }
        }


        
/// <summary>
        
/// 得到运算结果
        
/// </summary>
        
/// <returns></returns>

        public virtual double GetResult()
        
{
            
double result = 0
            
return result;
        }


       
    }

 

 

    /// <summary>
    
/// 加法类
    
/// </summary>

    class OperationAdd : Operation
    
{
        
public override double GetResult()
        
{
            
double result = 0
            result 
= NumberA + NumberB;
            
return result;
        }

    }


    
/// <summary>
    
/// 减法类
    
/// </summary>

    class OperationSub : Operation
    
{
       
public override double GetResult()
        
{
            
double result = 0;
            result 
= NumberA - NumberB;
            
return result;
        }

    }


    
/// <summary>
    
/// 乘法类
    
/// </summary>

    class OperationMul : Operation
    
{
        
public override double GetResult()
        
{
            
double result = 0;
            result 
= NumberA * NumberB;
            
return result;
        }

    }


    
/// <summary>
    
/// 除法类
    
/// </summary>

    class OperationDiv : Operation
    
{
        
public override double GetResult()
        
{
            
double result = 0;
            
if (NumberB==0)
                
throw new Exception("除数不能为0。");
            result 
= NumberA / NumberB;
            
return result;
        }

    }

        小菜:“大鸟哥,我按照你说的方法写出来了一部分,首先是一个运算类,它有两个Number属性,主要用于计算器的前后数,然后有一个虚方法GetResult(),用于得到结果,然后我把加减乘除都写成了运算类的子类,继承它后,重写了GetResult()方法,这样如果要修改任何一个算法,都不需要提供其它算法的代码了。但问题来了,我如何让计算器知道我是希望用哪一个算法呢?”
        大鸟:“写得很不错吗,大大超出我的想象了,你现在的问题其实就是如何去实例化对象的问题,哈,今天心情不错,再教你一招‘简单工厂模式’,也就是说,到底要实例化谁,将来会不会增加实例化的对象(比如增加开根运算),这是很容易变化的地方,应该考虑用一个单独的类来做这个创造实例的过程,这就是工厂,来,我们看看这个类如何写。”

 

    /// <summary>
    
/// 运算类工厂
    
/// </summary>

    class OperationFactory
    
{
        
public static Operation createOperate(string operate)
        
{
            Operation oper 
= null;
            
switch (operate)
            
{
                
case "+":
                    
{
                        oper 
= new OperationAdd();
                        
break;
                    }

                
case "-":
                    
{
                        oper 
= new OperationSub();
                        
break;
                    }

                
case "*":
                    
{
                        oper 
= new OperationMul();
                        
break;
                    }

                
case "/":
                    
{
                        oper 
= new OperationDiv();
                        
break;
                    }

             }


            
return oper;
        }

    }

          大鸟:“哈,看到吧,这样子,你只需要输入运算符号,工厂就实例化出合适的对象,通过多态,返回父类的方式实现了计算器的结果。”

Operation oper;
oper = OperationFactory.createOperate("+");
oper.NumberA 
= 1;
oper.NumberB 
= 2;
double result = oper.GetResult();
                

        大鸟: “哈,界面的实现就是这样的代码,不管你是控制台程序,Windows程序,Web程序,PDA或手机程序,都可以用这段代码来实现计算器的功能,当有一天我们需要更改加法运算,我们只需要改哪里?”
        小菜:“改OperationAdd 就可以了。”
        大鸟: “那么我们需要增加各种复杂运算,比如平方根,立方根,自然对数,正弦余弦等,如何做?”
        小菜:“只要增加相应的运算子类就可以了呀。”
        大鸟: “嗯?够了吗?”
        小菜:“对了,还需要去修改运算类工厂,在switch中增加分支。”
        大鸟: “哈,那才对,那如果要修改界面呢?”
        小菜:“那就去改界面呀,关运算什么事呀。”
        小菜:“ 回想那天我面试题写的代码,我终于明白我为什么写得不成功了,原来一个小小的计算器也可以写出这么精彩的代码,谢谢大鸟。”

(下为当时面试题时小菜所写代码,见《小菜编程成长记(一)》)

class Program
{
    
static void Main(string[] args)
    
{
        Console.Write(
"请输入数字A:");
        
string A = Console.ReadLine();
        Console.Write(
"请选择运算符号(+、-、*、/):");
        
string B = Console.ReadLine();
        Console.Write(
"请输入数字B:");
        
string C = Console.ReadLine();
        
string D = "";

        
if (B == "+")
            D 
= Convert.ToString(Convert.ToDouble(A) + Convert.ToDouble(C));
        
if (B == "-")
            D 
= Convert.ToString(Convert.ToDouble(A) - Convert.ToDouble(C));
        
if (B == "*")
            D 
= Convert.ToString(Convert.ToDouble(A) * Convert.ToDouble(C));
        
if (O == "/")
            D 
= Convert.ToString(Convert.ToDouble(A) / Convert.ToDouble(C));

        Console.WriteLine(
"结果是:" + D);
    }
     
}


        大鸟: “吼吼,记住哦,编程是一门技术,更加是一门艺术,不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简炼,更加容易维护,容易扩展和复用,只有这样才可以是真的提高。写出优雅的代码真的是一种很爽的事情。不过学无止境,其实这才是理解面向对象的开始呢。给你出个作业,做一个商场收银软件,营业员根据客户购买商品单价和数量,向客户收费。”
        小菜:“就这个?没问题呀。”

(待续)

posted on 2006-09-23 09:17 伍迷 阅读(12499) 评论(92)  编辑 收藏 所属分类: 面向对象小菜编程成长记

评论

#1楼   回复  引用    

简单明了......不错......
2006-09-23 12:02 | join[匿名][未注册用户]

#2楼   回复  引用  查看    

Operation oper;
oper.NumberA = 1;
oper.NumberB = 2;
oper = OperationFactory.createOperate("+");
double result = oper.GetResult();
-----------------------------------

这段有点问题吧,声明了但没实例化,就进行了oper.NumberA = 1;oper.NumberB = 2;
然后才调用工厂生成实例,顺序是不是放错了?
2006-09-23 13:53 | Amnoh      

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

@Amnoh
非常不好意思,的确是笔误,感谢您的批评指正。
2006-09-23 13:59 | 伍迷      

#4楼   回复  引用  查看    

很好,不错
2006-09-23 20:22 | 中国土匪      

#5楼   回复  引用  查看    

呵呵,挺好的,浅显易懂,我要让我们公司那些做了N年PB且从来不愿意学OOP的“高手”们来看看。

:) 博主下一章是不是要开讲Factory Method了?
2006-09-24 13:44 | leonqin      

#6楼   回复  引用    

public static Operation createOperate(string operate)
请问这句中的 Operation createOperate(string operate)是什么意思
谢谢
小菜不懂啊
2006-09-24 14:31 | kttt[未注册用户]

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

@kttt
Operation就是返回值是返回一个Operation对象。
createOperate是一个静态方法的名字,用来创建具体的运算对象
operate是一个字符串参数,代表输入的加减乘除符号
我不知道你对多态的理解有多少,这里就是用到了多态,即子类以父类的身份出现,却以自己的方式去实现。
这里就是加减乘除类以运算父类的身份出现,但每个子类实现GetResult()的方法是各自不同的,这样实现了界面代码根本不用去创建具体实例,达到松耦合的目的。
当你明白这个道理的时候,你会理解,面向对象,实在是太伟大的思想。
2006-09-24 15:53 | 伍迷      

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

@leonqin
不一定会按设计模式的顺序写,不过迟早会讲到,哈,通过故事来理解编程,是一种尝试,我会努力把后续写好。
2006-09-24 16:56 | 伍迷      

#9楼   回复  引用  查看    

谢谢
你的回答!
2006-09-26 08:37 | 释天      

#10楼   回复  引用  查看    

MARK
2006-09-26 11:03 | 心有灵犀      

#11楼   回复  引用    

请问:你的作业"商场收银"什么时间才完成啊?!!
2006-11-11 11:49 | yanwc[未注册用户]

#12楼   回复  引用    


把你的程序用java重写了一次....

import java.io.*;

abstract class Operation
{
double NumberA;
double NumberB;

abstract double getResult() throws Exception;
}

class OperateAdd extends Operation
{
public double getResult()
{
double result=0;
result=NumberA+NumberB;
return result;
}
}

class OperateSub extends Operation
{
public double getResult()
{
double result=0;
result=NumberA-NumberB;
return result;
}
}

class OperateMul extends Operation
{
public double getResult()
{
double result=0;
result=NumberA*NumberB;
return result;
}
}

class OperateDiv extends Operation
{
public double getResult() throws Exception
{
double result=0;
if (NumberB==0)
{
throw new Exception("除数不能为零");
}
result=NumberA/NumberB;
return result;
}
}

class OperateFactory
{
public static Operation createOperation(char oper) throws Exception
{
Operation op=null;
switch(oper)
{
case '+':
op=new OperateAdd();
break;
case '-':
op=new OperateSub();
break;
case '*':
op=new OperateMul();
break;
case '/':
op=new OperateDiv();

break;
default:
throw new Exception("操作的操作符有误码");

}
return op;
}
}
class OperateTest
{
public static void main(String[] args)
{

BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
try
{
System.out.print("请输入A的值");
String strNumberA=br.readLine();

System.out.print("请输入操作符,如(+,-,*,/)");
String strOperate=br.readLine();

System.out.print("请输入B的值");
String strNumberB=br.readLine();

Operation operate=OperateFactory.createOperation(strOperate.charAt(0));
operate.NumberA=Double.valueOf(strNumberA).doubleValue();
operate.NumberB=Double.valueOf(strNumberB).doubleValue();

String result=""+operate.getResult();

System.out.println("结果为"+result);
}
catch(Exception e)
{
e.printStackTrace();
}

}
}
2007-01-14 09:46 | QB[未注册用户]

#13楼   回复  引用    

QB这位老兄真有心,继续努力呀,C#和Java其实是殊途同归。
2007-01-28 21:33 | 伍迷[未注册用户]

#14楼   回复  引用  查看    

very good,很生动嘛,
2007-03-02 10:55 | chy710      

#15楼   回复  引用    

精彩!深入浅出!可解可行!谢谢!
2007-03-03 17:47 | 溢味轩[未注册用户]

#16楼   回复  引用    

大鸟: “那么我们需要增加各种复杂运算,比如平方根,立方根,自然对数,正弦余弦等,如何做?”
小菜:“只要增加相应的运算子类就可以了呀。”

==========

真的不用修改界面吗?那么平方根,立方根,自然对数,正弦余弦这些“符号”怎么输入呢?window自带的计算器可都是有相应的按钮的呀。你的是给一个文本框直接输入吗?
2007-03-03 23:11 | jyk[未注册用户]

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

@jyk
就这个例子而言,界面当然是要改的,不过完全可以改动非常之小,我指的更多的是其中的精神,而不是这个例子本身。
2007-03-03 23:15 | 伍迷      

#18楼   回复  引用    


老兄真适合做小儿节目主持人
2007-03-05 14:35 | 航天奇侠

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

@航天奇侠
哈,用初学者的视角,看到问题会比较容易一些,因为有些问题直接写,没感觉,如果是初学者,那么什么问题都有可能提得出来。再说设计模式的确是比较难理解的,刚学的时候有很多不明白的地方。
2007-03-05 14:50 | 伍迷      

#20楼   回复  引用    

有个地方可以修改。
当用户输入当前没有的运算符号时,oper返回的是空。
建议将加减乘除等运算种类封装成一个ENUM,这样其他开发人员拿过来,也知道能够处理哪些计算了。
2007-03-07 13:27 | ghibli[未注册用户]

#21楼   回复  引用    

浅显易懂 不错!!
2007-03-21 10:48 | jacky[未注册用户]

#22楼   回复  引用  查看    

very good ! mark
2007-03-22 09:35 | Ame      

#23楼   回复  引用  查看    

不好意思,第一个回复的时候只看了五,没有看见面的。
现在又从一到五看了一遍,有了不同的理解。

学了一招:是不是定义一个父类的对象,然后可以把子类的实力付给他。

这是不是就是多态了?

一直没有用过多态。试一试。
2007-03-22 14:19 | 金色海洋(jyk)      

#24楼   回复  引用  查看    

如果在
class OperationAdd : Operation

里面再加一个函数,比如 public int aaa()

那么在
Operation oper;
oper = OperationFactory.createOperate("+");

oper 是不是没有 aaa这个方法呢?

我做了一个程序,是没有aaa这个函数的,是不是正常的呀?

如果想让 oper也有aaa这个函数除了在父类里面定义之外还有没有其他的方法呢?
2007-03-22 14:40 | 金色海洋(jyk)      

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

@金色海洋(jyk)
你还没有真正理解多态,建议先去搜索“今天你多态了吗”的文章,再把计算器的代码一句一句的写一遍,或许你就能感悟多态的好处了。
2007-03-22 15:33 | 伍迷      

#26楼   回复  引用    

赞,喜欢你的文风
2007-03-24 16:45 | SnowDoggie[未注册用户]

#27楼   回复  引用  查看    

不错
不错,楼主说的非常幽默,通俗,把我们一步一步带入面向对象的世界
thx ^_^
2007-03-26 16:12 | Bryant      

#28楼   回复  引用    

很不错的文章
继续灌注
2007-03-26 20:11 | Six4[未注册用户]

#29楼   回复  引用    

文章写的很好.显明易懂.
2007-03-28 11:06 | gangzi[未注册用户]

#30楼   回复  引用    

学了很多东西 ,关注
2007-03-28 17:02 | wuyisky[未注册用户]

#31楼   回复  引用    

真正的高手是用最生动的语言,最简单的例子,这是真正的“深入浅出”。

赞!!!


老兄,加油,继续哟
2007-03-29 13:49 | jack[未注册用户]

#32楼   回复  引用  查看    

jack
同意楼上的说法
2007-03-29 20:11 | 兴百放      

#33楼   回复  引用  查看    

第三次看了。例子很好,但是在实际应用中一定会遇到很多问题的。
从来都没有用过多态,所以没有理解。一直做网站来着,没有遇到很复杂的逻辑。
对了函数重载算不算多态呢?如果不算的话函数重载是甚么呢?
初学OO,有很多不懂的问题。
2007-04-10 10:36 | 金色海洋(jyk)      

#34楼   回复  引用    

QQ332778169加我帮我一下
QQ332778169加我帮我一下
QQ332778169加我帮我一下

#35楼   回复  引用    

我试了工厂方法去实现怎么不行好象,。。
2007-07-26 20:05 | 超级菜鸟[未注册用户]

#36楼   回复  引用    

不错,继续努力.我会一直关注的.哈..
2007-07-28 15:38 | LeiJuan903[未注册用户]

#37楼   回复  引用    

不错的文章,简单明了,又不乏趣味
好的文章就得顶下
2007-07-31 22:45 | Jerry[未注册用户]

#38楼   回复  引用    

经常看,不支持下对不起良心哈
2007-08-01 18:13 | mady[未注册用户]

#39楼   回复  引用    

感觉还有switch语句,能否把switch去掉?
2007-08-01 21:14 | coder[未注册用户]

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

@coder
请参看后面的反射。可以去掉switch.
2007-08-01 22:10 | 伍迷      

#41楼   回复  引用    

很好的例子,即有故事情节,有趣,又明确的说明了oo即做法,不错,支持!
2007-10-16 22:06 | green spring[未注册用户]

#42楼   回复  引用    

真的是很不错,强烈支持。
2007-10-26 16:10 | tt1[未注册用户]

#43楼   回复  引用    

理解一下这个程序,几点体会:
首先是对控制台的操作,读入数据(字符串)。

然后创建Operation类对象oper ,相当于对oper符初值?

利用工厂模式OperationFactory类,对oper重新初始化。实例化NumberA、B,

最后调用oper.getResult()方法。

Operation 类,将运算与控制台程序独立开来(业务逻辑与界面逻辑分开)

即:封装。可以用于其它程序的接口。采用虚拟的getResult()方法,是为了

在继承类(具体的运算方法)中重构它。满足不同的运算要求。

OperationFactory 实际上是对计算方法的选择,这里根据不同的运算符实例

化不同的oper。这样做的好处是,要修改加减乘除运算,或者添加别的运算方法只需要对Operation 继承类中修改特定的算法,或者添加Operation 的集成类就可以了。这样做的目的是防止在修改过程中误修改了别的程序。也有利于程序的保密性,添加新的算法,不需要给写代码的人看源代码。
2007-10-30 15:31 | greeni[未注册用户]

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

@greeni
总结得很好,就是这个意思。
2007-10-30 16:01 | 伍迷      

#45楼   回复  引用  查看    

请教楼主,就这个实现运算而言,用策略模式是否更适合呢!
个人感觉策略模式本身就是可以替换的一组算法,而加、减、乘、除等运算刚好是适合的!
初学者诚心请教!
2007-11-27 10:25 | 天际翔龙      

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

@天际翔龙
你说得没有错,策略模式也适合于这个例子。这本身和简单工厂不矛盾,其实,最好的选择是简单工厂+策略模式。建议您继续往下看,或者有机会去阅读我的书,当中有对这做了解释。
2007-11-27 10:52 | 伍迷      

#47楼   回复  引用    

看了楼主的文章感触良多,唯一能做的就是对楼主万分的支持...!
2008-01-16 20:12 | mutouren[未注册用户]

#48楼   回复  引用    

你谢谢大鸟 偶谢谢你,...
2008-01-18 11:43 | 32131[未注册用户]

#49楼   回复  引用    

--引用--------------------------------------------------
jyk: 大鸟: “那么我们需要增加各种复杂运算,比如平方根,立方根,自然对数,正弦余弦等,如何做?”
小菜:“只要增加相应的运算子类就可以了呀。”

==========

真的不用修改界面吗?那么平方根,立方根,自然对数,正弦余弦这些“符号”怎么输入呢?window自带的计算器可都是有相应的按钮的呀。你的是给一个文本框直接输入吗?
--------------------------------------------------------

===================
应该是不用改业务逻辑程序部分吧
2008-03-19 11:38 | higo[未注册用户]

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

@higo
哈,对于Windows应用程序,当然是需要修改界面的,不过我这里写得是控制台的程序,对界面没有要求.

设计模式并不是说一定不改程序,只不过是尽量做到改动最小.
2008-03-19 12:09 | 伍迷      

#51楼   回复  引用    

父类与子类而产生的多态 很不错啊
2008-05-22 11:14 | 念时回复[未注册用户]

#52楼   回复  引用    

今天刚考了程序设计方法,里面就要求分析一个设计模式的,我也是挑的工厂模式,呵呵.......以后得多学学...
2008-06-13 21:34 | 白菜虫[未注册用户]

#53楼   回复  引用  查看    

谢谢lz指教~!~,有疑问请教:
老实说:多态我也知道怎么回事(重载,覆写);接口大概也知道,但都是概念级别的。一直想知道如何使用这些东西到实际项目中?看了你的系列文章,感触颇多,但关上IE,脑子里依然空空的。请lz指教,该如何做,才能好好理解并正确使用哪?
2008-06-16 17:30 | Ivan-Yan      

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

@Ivan-Yan
没在捷径,唯有多学多思多练也。
2008-06-17 09:08 | 伍迷      

#55楼   回复  引用    

精髓,灵魂
2008-06-20 15:46 | 王广庆[未注册用户]

#56楼   回复  引用    

写得真好,呵呵
2008-07-06 09:40 | 幸福[未注册用户]

#57楼   回复  引用  查看    

刚毕业,什么都不懂。一口气看了5篇,通俗易懂。也看了回复。突然发现应该学习greeni一样看完一些有价值的帖子后回复,这样也可以总结自己所学习到的东西,给自己思考的空间,加深理解。
1.首先是写了一个Operation类,定义两个私有字段和公有属性,字段不被修改。定义了一个虚拟方法(这个就有点不懂了,一步一步学,呵呵)
2、四个方法类继承了Operation类,重写了getresult。
3、定义一个OperationFactory类,createOperate方法实例化了Operation类。
4.进行控制台操作。
写得不好,楼主如果有空的话,可不可以指导指导从哪些方面总结比较恰当
2008-07-08 14:58 | Ivan-zheng      

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

@Ivan-zheng
你已经在进步了,很不错,继续努力。
2008-07-08 16:19 | 伍迷      

#59楼   回复  引用  查看    

太有收获了
2008-07-14 14:28 | 一根毛      

#60楼   回复  引用  查看    

最好源码也贴上
2008-07-22 16:56 | 爱.辉.舞      

#61楼   回复  引用    

那个做一个商场收银软件,营业员根据客户购买商品单价和数量,向客户收费。” 做了没有。。。。!~ 发我邮箱学习一下。。。

不胜感激!~  zhangchuandan01@163.com
2008-07-31 12:31 | 张传丹[未注册用户]

#62楼   回复  引用  查看    

继续关注 小菜和大鸟
2008-08-05 16:58 | 寻梦E.net      

#63楼   回复  引用  查看    

小菜:“改OperationAdd 就可以了。”
大鸟: “那么我们需要增加各种复杂运算,比如平方根,立方根,自然对数,正弦余弦等,如何做?”
小菜:“只要增加相应的运算子类就可以了呀。”
-----------------------------------------------------
最近在看大话模式,到这里有点疑惑.平方根,立方根等都是单操作数,不适合Operation啊.
2008-08-09 11:21 | 阿鹏      

#64楼   回复  引用  查看    

初学的菜鸟,感谢楼主的奉献精神,但是你的界面层直接就将
oper.NumberA = 1;
oper.NumberB = 2; 进行了赋值运算,让我觉得有点失落,楼主要是把计算器界面的代码 部分也发上来就更好了,实现动态运算,我们理解的就更深刻了。
但是在这里还是谢谢楼主的文章,写很很好!
2008-08-12 16:34 | 浪子の无悔      

#65楼   回复  引用  查看    

谢过楼主,原来大话一书的代码中提供了一个类windows计算器.
自己实现了下,明白了.
2008-08-17 20:40 | 阿鹏      

#66楼   回复  引用    

楼主,请总结一下简单工厂的好处在哪里吧
oper = OperationFactory.createOperate("+");
不是仍然跟字符串+绑定在一起吗
这跟我直接oper = new OperationAdd();
有什么区别呢
为什么多此一举来个工厂呢
2008-08-24 21:03 | href[未注册用户]

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

区别就在于
oper = OperationFactory.createOperate("+");
只需要一个字符串,而
oper = new OperationAdd();
需要知道具体的类。

有了简单工厂,你就可以通过一个字符串来决定实例化哪一个类,这就给扩展带来了方便。请再仔细阅读本篇博客,应该可以理解原因。如果还是不明白,你需要去补充面向对象的基础知识了。
2008-08-25 17:31 | 伍迷      

#68楼   回复  引用  查看    

收获很大,支持支持!!
2008-09-01 19:17 | 长空      

#69楼   回复  引用    

那就要用到复杂工厂模式了@金色海洋(jyk)
2008-09-23 13:47 | 顾客[未注册用户]

#70楼   回复  引用    

在C#中,抽象类可以实例化吗?
2008-10-10 11:53 | hutu0415[未注册用户]

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

@hutu0415
抽象类不可以实例化.
2008-10-10 14:14 | 伍迷      

#72楼   回复  引用    

谢谢 楼主。是我对多态的理解不够透彻,这回明白了。我是学Delphi的,为了看《大话》,临时抱佛脚学了一些C#语法。可还对一些语法和函数不理解,比如:
一、‘private Double MoneyRetate = 1d’应该是把MoneyRetate初始化为1d,这里的d是什么意思?
二、Convert.ToDouble()和double.Parse()应该是把目标数据转换为浮点数的函数,二者有何区别?
三、
public CashReturn(string MoneyCondition, string MoneyReturn)
{
this.MoneyCondition = double.Parse(MoneyCondition);//可以把字段this.MoneyCondition换成属性吗?变成对属性的读写。MoneyCondition可以声明为Double吗?以省去double.Parse()。
...
}

下面是我用Delphi改写的,请楼主帮忙看看也没有错误的地方?
//业务部分
unit Unit2;

interface

uses
SysUtils;

type
TCashSuper = class(TObject) //现金收费类
public
function AcceptCash(Money: Double): Double; virtual; abstract;
end;

TCashNormal = class(TCashSuper) //正常收费子类
public
function AcceptCash(Money: Double): Double; override;
end;

TCashRebate = class(TCashSuper) //打折收费子类
private
FMoneyRebate: Double;
public
constructor Create(MoneyRebate: string);
function AcceptCash(Money: Double): Double; override;
end;

TCashReturn = class(TCashSuper) //返利收费子类
private
FMoneyCondition: Double;
FMoneyReturn: Double;
public
constructor Create(MoneyCondition, MoneyReturn: string);
function AcceptCash(Money: Double): Double; override;
end;

TCashFactory = class(TObject) //现金收费工厂类
public
function CreateCashAccept(Category{种类}: Integer): TCashSuper;
end;

implementation

{ TCashNormal }

function TCashNormal.AcceptCash(Money: Double): Double;
begin
Result := Money;
end;

{ TCashRebate }

function TCashRebate.AcceptCash(Money: Double): Double;
begin
Result := Money * FMoneyRebate;
end;

constructor TCashRebate.Create(MoneyRebate: string);
begin
FMoneyRebate := StrToFloat(MoneyRebate);
end;

{ TCashReturn }

function TCashReturn.AcceptCash(Money: Double): Double;
begin
Result := Money;
if (Money >= FMoneyCondition) then
Result := Money - Round(Money / FMoneyCondition) * FMoneyReturn;
end;

constructor TCashReturn.Create(MoneyCondition, MoneyReturn: string);
begin
FMoneyCondition := StrToFloat(MoneyCondition);
FMoneyReturn := StrToFloat(MoneyReturn);
end;

{ TCashFactory }

function TCashFactory.CreateCashAccept(Category: Integer): TCashSuper;
var
CashSuper: TCashSuper;
begin
CashSuper := nil;
case (Category) of
0: CashSuper := TCashNormal.Create; //正常收费
1: CashSuper := TCashReturn.Create('300', '100'); //满300百返100
2: CashSuper := TCashRebate.Create('0.8'); //打8折
end;
Result := CashSuper;
end;

end.

//界面部分
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, ExtCtrls;

type
TForm1 = class(TForm)
LblPrice: TStaticText;
LblNumber: TStaticText;
EdtPrice: TEdit;
EdtNumber: TEdit;
LblCategory: TStaticText;
CboCategory: TComboBox;
BtnOK: TBitBtn;
BtnClear: TBitBtn;
LstList: TListBox;
StaticText4: TStaticText;
LblResult: TStaticText;
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure BtnOKClick(Sender: TObject);
procedure BtnClearClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

uses
Unit2;

var
Total: Double;
aCashSuper: TCashSuper;
aCashFactory: TCashFactory;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
//aCashSuper := TCashSuper.Create;
aCashFactory := TCashFactory.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
//aCashSuper.Free;
aCashFactory.Free;
end;

procedure TForm1.BtnOKClick(Sender: TObject);
var
TotalPrice: Double;
begin
aCashSuper := aCashFactory.CreateCashAccept(CboCategory.ItemIndex);
TotalPrice := aCashSuper.AcceptCash(StrToFloatDef(EdtPrice.Text, 0)) * StrToFloatDef(EdtNumber.Text, 0);
Total := Total + TotalPrice;
LstList.Items.Add('单价:' + EdtPrice.Text + ' 数量:' + EdtNumber.Text + ' '+
CboCategory.Items.Strings[CboCategory.ItemIndex] + ' 合计:' + FloatToStr(TotalPrice));
LblResult.Caption := FormatFloat('0.00', Total);
end;

procedure TForm1.BtnClearClick(Sender: TObject);
begin
EdtPrice.Text := '0';
EdtNumber.Text := '0';
CboCategory.ItemIndex := 0;
LstList.Items.Clear;
Total := 0;
LblResult.Caption := FormatFloat('0.00', Total);
end;

end.
2008-10-11 12:51 | hutu0415[未注册用户]

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

@hutu0415
技术细节建议您查书或者利用好搜索引擎.
2008-10-14 09:07 | 伍迷      

#74楼   回复  引用  查看    

买了<设计模式>这书了,没时间看,先看看文章,很不错,买的书挺值的
2008-10-16 16:46 | love&tiger      

#75楼   回复  引用  查看    

非常好,支持楼主.
读君几篇文,胜过十本书啊
2008-10-27 18:01 | 依依之恋      

#76楼   回复  引用  查看    

@伍迷老兄
呵呵,问个题外话。您不是伍佰的fans吧?

在你的附书代码中“计算器”有一小处瑕疵:
输入多于两个点的时候,显示框会清掉。
CheckNumberInput应该添加:
else
{
result = currentNumber;
System.Media.SystemSounds.Beep.Play();
}
如下:
public static string checkNumberInput(string currentNumber, string inputString)
{
string result = "";
if (inputString == ".")
{
if (currentNumber.IndexOf(".") < 0)
{
if (currentNumber.Length == 0)
result = "0" + inputString;
else
result = currentNumber + inputString;
}
else
{
result = currentNumber;
System.Media.SystemSounds.Beep.Play();
}
}
else if (currentNumber == "0")
{
result = inputString;
}
else
{
result = currentNumber + inputString;
}

return result;
}
2008-11-12 14:14 | DylanWind      

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

@DylanWind
谢谢指正。

伍迷的意思其实是很早前(大约99年)玩过一个台湾的五子棋RPG单机游戏。里面的主角叫伍迷。我当时天天在玩,被同事说我跟伍迷完全一个样。当时接触上网需要网名,就想到这个名字了,本来只是一时用用,不过我是个传统之人,用了一般就不改,所以就一直延用至今。
2008-11-12 15:25 | 伍迷      

#78楼   回复  引用  查看    

@伍迷
哈哈 原来你是从一而终的人啊。
2008-11-13 09:40 | DylanWind      

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

@DylanWind
是的,记得我是2000用手机的,当时买了一个联通的如意通卡,8年了,手机换了好几个(主要是用到后面不能用了),但是手机号是一直没变的。有些东西不应该常换的。
2008-11-13 12:21 | 伍迷      

#80楼   回复  引用    

不知道有没有建立的交流群呀?
2008-11-23 20:12 | wyl918[未注册用户]

#81楼   回复  引用  查看    

@href
对于使用代码的客户来说,不需知道具体的类,只需知道“+”,就能够正确使用了,觉得把“+”,“-”等发到一个枚举里会更好。呵呵。是不是呢?
2008-12-20 12:26 | 会长      

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

@会长
是这样的。
2008-12-21 15:32 | 伍迷      

#83楼   回复  引用  查看    

那个计算器的代码条理清楚,但是我仍然不能让它计算一下,够笨吧~~能不能给个完整版的……楼主的思想支持一下!书不错。
2009-02-20 10:26 | 月光宝盒      

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

@月光宝盒
请下载《大话设计模式》的源代码,第一章里就有。
http://www.cnblogs.com/cj723/archive/2007/12/30/1021314.html" target="_new">http://www.cnblogs.com/cj723/archive/2007/12/30/1021314.html
2009-02-20 11:42 | 伍迷      

#85楼   回复  引用  查看    

写得太棒了!!!
今天去面试时,就问了我这个问题(关于简单工厂模式),可是我一直对设计模式没有研究过(其实之前也看过一点,但是理解不了,放弃了)。
看了你的大作,我受益非浅啊,今天终于彻底理解了!多谢!!
2009-03-27 00:06 | jeky      

#86楼   回复  引用    

樓主您好,
拜讀您的大作後真是受益良多, 但有個地方比較不清楚, 簡單工廠模式中的簡單工廠類別與運算類別是屬於關聯關係還是依賴關係??...在書上看到兩種關係都有出現在同一個模式下
2009-05-18 16:56 | ^^

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

@^^
准确地说,应该是依赖。
2009-05-18 17:11 | 伍迷      

#88楼   回复  引用    

class Operation有必要定属性 private double _numberA = 0; private double _numberB = 0;吗?
传两个参数不就行了?
2009-05-20 11:21 | TaoistWar[未注册用户]

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

@TaoistWar
都是可以的
2009-05-20 12:45 | 伍迷      



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 512430




相关文章:

相关链接: