锁定老帖子 主题:OO design trap
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-12-26
partech 写道 age0 写道 这种设计显然就是设计1的变种,只不过是将原来的Member拆成两部分,本质上依然是过程设计。OO的核心价值是封装、继承及多态,仅从封装角度来细分职责我认为是不足以体现OO价值的。
难道,你把使用member属性的所有操作都放入member中就能体现封装? 而“把稳定和变化的部分分离”,就不需要考虑? 正好俺正在研究Eclipse,其中有一个很重要的接口IAdaptable,它允许你通过扩展的方式,为对象添加行为。这无疑是“把稳定和变化部分分离”最佳示例。 我的意思是分离并不足够,分离仅仅使用了封装特性,你的设计并没有体现继承、多态等正统OO价值,只有封装特性的OO无疑是半调子的OO。 扩展方式为对象添加行为我是不大赞成的,差不多又是打补丁的方案了,违背了上帝的设计理念呀,我们的上帝总不会心血来潮的去为原子扩展特性吧。 |
|
返回顶楼 | |
发表时间:2005-12-26
age0 写道 各位有兴趣提供解决方案的同志注意了,我们所面对的需求环境是极端恶劣的,你甚至可以认为超市boss是非常evil的,不要怀疑该boss会提出种各样让你诧异到想跳起来狠揍他但是从商业角度上考虑又合情合理的古怪需求,我们的忠告只有一个:prepare for everything。
评判设计优劣的标准主要有两个: 一是绝对的代码变更量,包括新增或修改的代码量,另外即使是修改xml之类的配置文件同样纳入变更范围,改代码和改配置并无本质区别。 二是变动所牵涉的范围:对原有代码的修改应该减至最少,尽可能逼近纯增量,修改所牵涉的类越少越好,并且应该避免引发接口变动。 就以OO设计二为例,如果需求仅仅是围绕member type发生变动,那么该设计无疑是符合要求的。只是不幸的是,后来的需求变动超出了设计的预估范围,以至画虎不成反成猫。 对于这种需求,接口变化大的话,不单变成猫,可能老鼠也有可能。 比较喜欢CO的方式。 可以在里面加入 任何需要判断折扣点多少的依据 和 怎么判断的逻辑 。 对于不能抽象的部分就不要封装。 |
|
返回顶楼 | |
发表时间:2005-12-26
Quake Wang 写道 partech 写道 这里假设,折扣只同member和order的信息有关,那么将来的任何变化,都可以只从getDiscount扩展出去。 这又是一个“概念依赖”的例子,好的DomainModel,就是职责划分合理的结构,而能打开这扇门的钥匙,我认为就是“概念依赖”。 同意partech的观点,不应该将什么东西都塞到Entity Object里面,应该做到责任分明。 我所知道有2种常见的处理可以解决这种用户可定制策略: 1. buaawhl给的CO 2. script,script如果做得完善的话,比如建立在CO上的script,就可以称之为DSL了(Domain Specific Language) 附件是用beanshell做的一个非常简单的script,来解决age0提出的情景。 C++有没有象bean shell 这样的东东? 如果性能要求很高的系统, C++怎么解决比较好? |
|
返回顶楼 | |
发表时间:2005-12-26
age0 写道 我的意思是分离并不足够,分离仅仅使用了封装特性,你的设计并没有体现继承、多态等正统OO价值,只有封装特性的OO无疑是半调子的OO。 扩展方式为对象添加行为我是不大赞成的,差不多又是打补丁的方案了,违背了上帝的设计理念呀,我们的上帝总不会心血来潮的去为原子扩展特性吧。 俺的方法,是要解决面临的问题。 至于你说的体现正统OO价值,我想是大学老师需要考虑的问题。而且,价值总是同它要解决的问题相关的,如果问题没有相关的要求,那这种为体现而体现的做法,就是画蛇添足。 至于你所说“违反上帝的设计理念”的说法,更是荒唐。软件结构中体现的总是基于人某个问题或某个意图的解决方案,这意味着其必然体现人的主观抽象。要知道软件中存在的对象也好,方法也好,不过是人根据主观意图对真实对象的某种描述。其本质并不等同于真实对象的本质;而是事物被人所关心的方面,决定了软件中对象的本质。 认为软件中的对象可以“全息模拟”现实世界中存在的对象,无疑犯了“和尚数人”错误,软件是一种人创造的产物,离开人而单独的考虑对象,只可能导致不可知的结果。“人抽象对象,来达到某种目的”才是考察对象的正解。 你试图“用上帝的设计理念”来开发软件,难道people还有个getAtoms的方法不成?然后Atom再来一个getQuarks?Quark再来一个啥子方法? 不过很抱歉。你“上帝式的设计理念”,却不能给你带来上帝无所不能的感觉,这岂不是个讽刺? |
|
返回顶楼 | |
发表时间:2005-12-26
看了源码,貌似发现了T1的一个马甲:wintereagle
|
|
返回顶楼 | |
发表时间:2005-12-26
参考了T1的CO和Quake Wang的script之后,得出OO设计3。
// client string id = input_id; member_agent ma = new member_agent(id);; int discount = ma.GetDiscount();; int return_point = ma.GetReturnPoint();; // service // member class Member { string type; string gender; public Member GetMember(string id);; } // strategy class member_strategy { virtual public double GetDiscount(Member member);; virtual public double GetReturnPoint(Member member);; } class golden_strategy : member_strategy { override public double GetDiscount(Member member);; override public double GetReturnPoint(Member member);; } class silver_strategy : member_strategy { override public double GetDiscount(Member member);; override public double GetReturnPoint(Member member);; } class female_strategy : member_strategy { override public double GetDiscount(Member member);; override public double GetReturnPoint(Member member);; } // stragetys class member_strategys { static public ArrayList GetStrategys(Member member); { ArrayList list = new ArrayList();; switch(member.type); { case "金卡": list.Add(new golden_strategy);; break; case "银卡": list.Add(new silver_strategy);; break; } switch(member.gender); { case "女": list.Add(new female_strategy);; break; } } } // agent class member_agent { ArrayList m_stragegys; Member m_member; public member_agent(string id); { // m_member = Member.GetMember(id);; // m_stragegys = member_strategys.GetGetStrategys(member);; } double GetDiscount(); { int discount = 0; foreach(member_strategy ms in m_stragegys); discount += ms.GetDiscount(m_member);; return discount; } double GetReturnPoint(); { int return_point = 0; foreach(member_strategy ms in m_stragegys); return_point += ms.GetReturnPoint(m_member);; return return_point; } } |
|
返回顶楼 | |
发表时间:2005-12-26
age0 写道 这种设计显然就是设计1的变种,只不过是将原来的Member拆成两部分,本质上依然是过程设计。OO的核心价值是封装、继承及多态,仅从封装角度来细分职责我认为是不足以体现OO价值的。 没有服务层,你Domain用得起来么?就我一些微薄的经验,我觉得即使是在Domain里面用服务类也未尝不可。比如一些规则计算,比如有些业务边界对象(在我这个系统里面不是重点,在其它系统里面可能有个复杂的对象模型),我没办法实例化它(比如仓库),对我的Core Domain来说,我用这些业务边界对象和用服务的感觉也没什么区别了(系统还没SOA)。 可能我们需要先划分清楚,我们这里是考虑为了解决实际问题的OO还是纯理论(完美?)的OO |
|
返回顶楼 | |
发表时间:2005-12-26
wolfsquare 写道 看了源码,貌似发现了T1的一个马甲:wintereagle
太老了,那个counterpoint不知道还在写没有 |
|
返回顶楼 | |
发表时间:2005-12-27
age0这个例子应该再复杂点,否则不足以说明co的好处。
一个完整的co一般会要有bind, map等操作,这个例子还太简单,用不到真正co的灵活性。 大致的客户代码可以长成这样: Computation def = Computations.retn(0.0);; Computation bytype = cardtypecase( new String[]{"gold", "silver"}, new double[]{0.01, 0.03}, def);; Binder bygender = ifelse( or( is_date("3/8");, if_gender("female");, );, inc(0.05); );; Computation result = Computations.bind(bytype, bygender);; |
|
返回顶楼 | |
发表时间:2005-12-27
ajoo 写道 age0这个例子应该再复杂点,否则不足以说明co的好处。
一个完整的co一般会要有bind, map等操作,这个例子还太简单,用不到真正co的灵活性。 大致的客户代码可以长成这样: Computation def = Computations.retn(0.0);; Computation bytype = cardtypecase( new String[]{"gold", "silver"}, new double[]{0.01, 0.03}, def);; Binder bygender = ifelse( or( is_date("3/8");, if_gender("female");, );, inc(0.05); );; Computation result = Computations.bind(bytype, bygender);; 这点倒不必担心,需求从来都是由简单发展到复杂的,逐渐增加需求复杂度的目的是要从不同阶段去考虑设计的各种可能性,展现设计的艺术。 有一点要提醒一下,大家似乎把策略看得过于简单了,复杂的策略并不只是会返回一个固定的数值,可能会是根据会员的多项信息(例如本月累计购买额)综合计算出来,因此必须考虑“golden”对应某个算法而不是“0.1”的情况。 |
|
返回顶楼 | |