论坛首页 Java企业应用论坛

OO design trap

浏览 55129 次
锁定老帖子 主题:OO design trap
该帖已经被评为精华帖
作者 正文
   发表时间:2005-12-24  
buaawhl 写道
OO 多态的作用就是用来消除 hard coded logic branch (if, else, switch)。

如果使用了hashtable, 这几种做法都消除了 hard coded logic branch ,那OO多态的优势,自然就不存在了。那么根本就不需要使用 OO多态。

如果题目的本意,就是表面给出的这么简单基础,使用hashtable就能很好的解决,我估计大多数具有基础基本功的人都知道如何解决,那么这个帖子还有什么意义?

既然楼主给出了这个例子。我们自然假设这是一个足够复杂需求的简单示意。hard coded logic branch 无法简单的使用hashtable来消除。在这样的假设的基础上,才具有讨论的意义。

类的抽取足够必要,但是多态的抽取就看具体的情况了。
0 请登录后投票
   发表时间:2005-12-26  
我的习惯是,多用组合、接口,慎用继承,必要的话使用服务性质的类


-----Server------

//感觉通过Member去获取折扣有点别扭,提到Payment里面了
class Memeber
{
	protected string m_type = "";
	
	public string Type
	{	
		get
		{
			if(m_type == "");
			{
				GetType();;
			}
			return m_type;
		}
	}

	public string GetType();
	{
		// from db or somewhere else
	}
}


class Payment
{
	//Payment未必是一个需要实例化的类,提供一个类似服务的静态接口
	public static decimal GetDiscount(Memeber member);
	{
		Discount discount = null;
		    
                switch(member.Type); 
                { 
                        case "金卡": 
                               discount = new GoldenDiscount();;
                        
                        case "银卡" 
                               discount = new SilverDiscount();;
                } 

		return discount.GetDiscount();;
	}

}

//如果Discount的计算规则够简单的话,不需要这个继承树
class Discount 
{
	virtual protected decimal GetDiscount();
	{
		return 0;
	}

	//如果计算Discount的时候需要member的某些信息的话
	virtual protected decimal GetDiscount(Member member);
	{
		return 0;
	}
}

class GoldenDiscount :Discount
{
	//overrdie 
	。。。。
}

class SilverDiscount : Discount
{
	//overrdie 
	。。。。
}


-----Client

string id = input_id; 
Member member = new Member(id);; 

int discount = Payment.GetDiscont(member);;
0 请登录后投票
   发表时间:2005-12-26  
如果用接口,修改的地方就是具体实现,那不是得修改更多?
0 请登录后投票
   发表时间:2005-12-26  
如果用C++, 我会用你的第2种设计.
如果用java, 第二种设计可以通过反射机制, 或者干脆用spring, 把type决策的if else也去掉
0 请登录后投票
   发表时间:2005-12-26  
buaawhl 写道
最后一个的 switch 最少,就选最后一个。

每次添加一个新的类型,所有hard coded switch的地方都需要修改。修改的地方,当然越少越好。最好就是只添加新类,不改动任何旧类代码。


OO设计2如果用C++做必须修改type decider.
但是如果用java可以做到只修改配置文件不重新编译

这样的系统我碰到的大多是用C++做的, 一般必须修改一个类. OO设计2对C++来说是一个不错的设计
0 请登录后投票
   发表时间:2005-12-26  
interface DiscountStrategy {
  double getDiscount();;
  int getReturnPoint();;
}

class GoldenDiscount implements DiscountStrategy {
  // 实现
}

class SilverDiscount implements DiscountStrategy {
  // 实现
}

// 普通会员
class Member {  
  DiscountStrategy strategy;

  Member(DiscountStrategy strategy); {
    this.strategy = strategy;
  }

  double getDiscount(); {
    return strategy.getDiscount();;
  }

  int getReturnPoint(); {
    return strategy.getReturnPoint();;
  }
}

// 女性会员
class FemaleMember extends Member {  
  DiscountStrategy strategy;

  Member(DiscountStrategy strategy); {
    super(strategy);;
  }

  double getDiscount(); {
    double discount = strategy.getDiscount();;
    // 计算附加优惠
   return discount ;
  }
}


某天boss心血来潮,给大于60岁的男人某种特殊优惠,那我就再给他加一个特殊的Member子类。某天boss心血来潮,搞出一大堆的乱七八糟的组合,那我就给他重构到Decorator模式。
0 请登录后投票
   发表时间:2005-12-26  
T1 写道

俺们来CO一把


附件里面是T1的一个Discount CO实现。
0 请登录后投票
   发表时间:2005-12-26  
如果增加白金卡, 钻石卡, 铜卡, 铁卡, 铝卡
你还是要修改你的fill_type_stream , 如果用C++你的这种设计非常认同, 如果用java应该改进一把, 总归修改代码有点bed smell(虽然大多数情况下不可避免)
0 请登录后投票
   发表时间:2005-12-26  
zengjin8310 写道
如果增加白金卡, 钻石卡, 铜卡, 铁卡, 铝卡
你还是要修改你的fill_type_stream , 如果用C++你的这种设计非常认同, 如果用java应该改进一把, 总归修改代码有点bed smell(虽然大多数情况下不可避免)


T1 写道

为什么非要修改,添加不行么?
0 请登录后投票
   发表时间:2005-12-26  
partech 写道

呵呵,我看问题不是什么是最好OO设计的问题。而是如何划分职责的问题。
正如 evanyuan指出:“感觉通过Member去获取折扣有点别扭”,你在这里
职责划分有问题,就导致了你后面子类爆炸的结果。
注意:你要解决的问题是根据member的属性,提供复杂折扣和返点的计算。
也就是说变化的是这种依赖member的计算,而不是member本身有了什么变化。所以不要再瞎折腾member了。
就只以折扣为例,简单的实现如下:

public class Payment{

   public Decimal pay(Member member, Order order);{
      Decimal amount;
       //计算实际总额
    ......
      return amount * getDiscount(member,order);;
}

   private Decimal getDiscount(Member member, Order order);{
                Decimal discount;
              switch(member.type); { 
                        case "金卡": 
                            discount = ...;                            
                        case "银卡" :
                             discount = ...;                             
                } 

              switch(member.sex); { 
                        case "女": 
                            discount += ...;                            
                } 
   }
   
}

这里假设,折扣只同member和order的信息有关,那么将来的任何变化,都可以只从getDiscount扩展出去。
这又是一个“概念依赖”的例子,好的DomainModel,就是职责划分合理的结构,而能打开这扇门的钥匙,我认为就是“概念依赖”。


这种设计显然就是设计1的变种,只不过是将原来的Member拆成两部分,本质上依然是过程设计。OO的核心价值是封装、继承及多态,仅从封装角度来细分职责我认为是不足以体现OO价值的。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics