尽量不要使用if else, freebox的那个类Visitor模式可以解决if else的问题。

freebox上面的代码中怎么回到if else了呢?OderItem和Checker之间如果不用静态关联就更好。因为从业务角度上讲,Checker和Orderitem确实存在很强的业务逻辑关联,所以应该理直气壮地使用正常类关联或wrapper或桥模式。

我是想修改killer的部分代码,然后一步步继续,先把这个弄到外面来。
我自己的设计在附件里,可惜附件的url部分没能正确显示啊,只好在下面又直接贴了个地址。我还想让大家指点指点有什么不足呢,因为以前一直都是这么写的,可能有不对的地方,望大家及时指出,我好改正。
是我上传的操作不正确吗?如果是请指点指点正确方法。
如果是论坛的bug,望大家能修复这个bug。
刚刚发现,自己的设计里的确是visitor模式,以前一直都不知道,不过一直这么用了。
[该贴被freebox于2008-06-23 11:16修改过]

我觉得这个地方是不是可以设计的更简单点。我感觉这个检查逻辑实际跟Cagegory有关,大家看看只用一个简单的Strategy是不是也能解决问题:
class Category
{ CheckStrategy strategy;...}

interface CheckStrategy
{
public void check();
}

NeverCheckStragety implements CheckStrategy{...}
AlwaysCheckStragety implements CheckStrategy{...}

[该贴被power1128于2008-06-23 13:25修改过]

产品是产品,产品库存是产品库存,库存检查是下单中的逻辑,跟产品和库存毫无关系。
如果你硬要把库存检查的逻辑写到产品和库存里,我只能认为你在走向另一个极端

我觉得“是否需要库存检查”的逻辑和“库存检查”的逻辑,是两个问题吧?

我表达的不好,“是否需要库存检查”属于下单中的逻辑,跟其它无关。
“库存检查”是产品提供的方法,但仅此而已。他不需要关心谁来调用和什么时候调用,当然也不关心类似于产品类别这样的逻辑。

但是按照楼主的描述,是否需要库存检查是和具体的类别有关的,就是说一旦产品的类别被确定了,无论是否下单,它都能够确认是否需要库存检查。

-->>但是按照楼主的描述,是否需要库存检查是和具体的类别有关的,就是说一旦产品的类别被确定了,无论是否下单,它都能够确认是否需要库存检查。

是的, 检查库存不只是在下单时才用上,可能在其它的地方要用.

如果是这种内在的逻辑,那是应该绑定在所在的类上面。有一个前提条件,类的粒度应该与方法相同。换句话说,该方法不应依赖于实例而存在。
就当我前面什么没说过吧。
[该贴被accipiter于2008-06-23 15:58修改过]

我认为power1128的方法是可行的,但是和我第一次的回复一样,您的Category一定要具备“检查类别”的意义。否则产品类别众多,策略实现会多得很。或者在每次操作时都指定而非查找一个策略。


class Category{
CheckKind kind;
}
class CheckKind{
CheckStrategy strategy;
}
interface CheckStrategy{
boolean needCheck(OrderItem item);
}
class NeverChecker implements CheckStrategy{
boolean needCheck(OrderItem item){
return false;
}
}
class CycleChecker implements CheckStrategy{
boolean needCheck(OrderItem item){
if(item.getCycle()<30)return false;
return true;
}
}
class EkeOrderItem extends OrderItem{
private ProductRepository productRepository;
public boolean hasEnoughStock(){
if(getCategory().getCheckKind().getStrategy().needCheck(this)){
Long count=productRepository.findCount(getProduct());
if(count<getProductCount()){
return false;
}
}
return true;
}
}

[该贴被freebox于2008-06-23 16:13修改过]

我重新再看楼主业务含义,其实有三层:
类别 库存检查前条件 库存检查后的决定

这三层决定了OrderItem是否下单,后来楼主补充业务需求:
检查库存不只是在下单时才用上,可能在其它的地方要用。

那么OrderItem就不是这三层逻辑组合的唯一客户端,至少这三层逻辑不应该放在OrderItem中。

这样,类别需要抽象的,类别中有一个“该类别的库存处理逻辑”。


class Category{
CheckStrategy strategy;
}

CheckStrategy 就包括两个:库存检查前条件(是否需要检查库存) 库存检查后的决定

boolean isNeedCheck();//是否需要检查库存
boolean requestIsPassed();//库存检查后的决定,下单要求是否通过

再看看需求,将来可能变化的是:产品类别 以及 该类别的库存处理逻辑( 库存检查前条件 和 库存检查后的决定),不变的是:确定的类别对应确定的类别的库存处理逻辑。

我们将变化的作为Category子类实现,ACategory; BCategory; CCategory; 。。。。

这里库存处理逻辑策略也会变化,因此,CheckStrategy 也有ACheckStrategy; BCheckStrategy ;CCheckStrategy ....

class ACategory{
CheckStrategy strategy = new ACheckStrategy();//确定了ACheckStrategy
}

class BCategory{
CheckStrategy strategy = new BCheckStrategy();//确定了BCheckStrategy
}

那么在具体客户端调用中,比如OrderItem中不应该耦合ACategory或ACheckStrategy等。只和Category和
CheckStrategy 两个接口或抽象有关。

OrderItem中肯定包含产品信息,产品肯定有具体Category信息,也就是已经确定了其是ACategory或BCategory,那么无疑我们就可以直接使用语句判断是否可下单:

getCategory().getStrategy().requestIsPassed();

到这里,我发现要调整一下CheckStrategy 接口内容,boolean isNeedCheck();//是否需要检查库存没有必要放在CheckStrategy中,因为isNeedCheck只是是否可下单的内部一个判断条件。也就是说isNeedCheck并入requestIsPassed中即可。

到这里,我们整理出基本的类结构,好像没有必要使用设计模式,其实模式是为将来考虑的,使用模式的前提是先将现有的结构整理分析出来,然后再加上将来变化的考虑,这里将来变化的是类别和策略,因为我们已经使用接口实现方式来应付类别的增加变化,下面还有一个收口的问题,就是具体类别的创建不能耦合在客户端中,比如这里的OrderItem。

其实具体的OrderItem中的已经和Product聚合关联,而Product可能和Category聚合关联,也就是说:从OrderItam诞生那天起,已经有具体Category了,所以,在OrderItem中就不用考虑Category具体实例的创建了。

有一个地方可以使用模式的,就是 CheckStrategy strategy = new ACheckStrategy();
我们可以将不同的检查策略和类别进行组合,我这里写死了,比如使用策略模式。
访问者模式可以应付在类别种类不变情况,也就是基本就ACategory BCategory,而CheckStrategy 变化很多,那么使用访问者模式可以。但是如果类别种类也在新增,那传统的访问者模式就不适合了。

但是前面的freebox的:
abstract class Category{
abstract void accept(CheckStock cs);
}
不是传统意义上的访问者模式,所以,我说类visitor模式,这个Category接口和我上面直接将CheckStrategy strategy放入Category中的区别是:可以把库存检查逻辑从具体的ACategory中分离出去,达到可能重用CheckStrategy 目的,这是好的。

下一步再结合策略模式,就可以灵活地挑选不同CheckStrategy 和不同Category结合,达到不同组合变化目的。



[该贴被banq于2008-06-23 19:24修改过]

Category和CheckKind是否都需要呢?一个是产品的原生类别,另一个是产品的库存检查类别,而CheckKind可能不只包含一个Category,而是一批Category,我自己认为CheckKind是一个业务对象。把它弄出来之后,检查类别也就不多了,策略的实现也少了。
如果直接把策略接口放在Category中,那么取得某个OrderItem之后,应该如何取得它对应的策略呢?是需要人工设置一个策略,还是这个策略也被注册在策略选择器列表里?即使注册了,也可能是多个Category对应同一个策略,这样仍然需要进行策略的选择。
我对模式只了解一部分,也许在代码中体现了,但并不知道其实就是某个模式。还有上帖的那个EkeOrderItem不知能否如此设计,还望大家批评指正。

很惊喜,这么多高手参与讨论. 让我受益匪浅.

从大家的讨论中,有一些体会: 有的人虽然模式不是很了解.但在实践过程中,不知不觉就用上了模式,
这说明OO已经达到一定的境界了.我们更需要的就是这种境界. 有的人了解了设计模式,但是分析问题能力没跟上,比如像我,最终用模式的眼光看问题.想着用某个模式能不能套上去.这已经是本末倒置了, 虽然用模式解决了问题.但是最终走上了过渡设计的道路.从该讨论中就可以体会倒.一开始我以为可以用bridge可以解决,最后freebox的Vistor,到最后BANQ的不用设计模式.还是要感叹BANQ的分析能力.几下就把问题分析到关键点上.


最后还要说一下讨论的这个问题,最终得判断是少不了的,关键是判断怎么处理.

认为很好的解决检测部分的代码。
public class Product {

private long id;

private String name;

private String description;

private ProductCategory category;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public ProductCategory getCategory() {
return category;
}

public void setCategory(ProductCategory category) {
this.category = category;
}
}


public class ProductCategory {

private long id;

private String description;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}
}


public interface ProductSpecification {


void validate(Product prod)throws ValidationException;
}

public interface ProducutSpecficationComposite extends ProductSpecification{

void addChildSpecification(ProductSpecification spec);
}


public class ProdcutSpecficationContainer implements ProductSpecification,ProducutSpecficationComposite{

private List<ProductSpecification> pcl=new LinkedList<ProductSpecification>();

public void validate(Product prod) throws ValidationException {

for(int i=0;i<this.pcl.size();i++){
ProductSpecification psf=pcl.get(i);
psf.validate(prod);
}
}

public void addChildSpecification(ProductSpecification spec) {
this.pcl.add(spec);
}
}

public class AProductSpecification implements ProductSpecification{

public void validate(Product prod) throws ValidationException {
//check it
}
}


public class BProductSpecification implements ProductSpecification{

public void validate(Product prod) throws ValidationException {
//check product
}
}

大家分析得很好,我个人补充一点看法:

应该在产品类别的管理与库存检查逻辑之间做一个权衡,如果产品类别很少,而且比较固定,并且对每一类别的产品的库存检查都比较关注,则可以采取从ProductCategory派生出ACategory, BCategory,CCategory等子类的方法,将具体的库存检查逻辑和具体的ProductCategory类关联,这样做的缺点是对产品类别的管理不灵活,新增一个产品类别就要增加一个类,造成类过多,而且对以后的统计报表等信息的处理不方便。如果想灵活管理产品类别,而且对库存检查逻辑关注的产品类别比较少,只是对几个大类的库存检查比较关注,则可以采取我的设计方法,这里我需要对ProductCategory补充修改一下:
//产品类别
public class ProductCategory implements Serializable {
private String ID;
private String code;
private ProductCategory fatherCatgy; //产品父类别

public boolean isCatgyof(ProductCategory fatherCatgy){};
}
库存检查逻辑中的
if (product.getCategory().getCode().equals("A"))
改为if (product.getCategory().isCatgyof(aCategory))

其实产品类别是一个典型的组合模式,用户可以灵活管理产品类别。但是这样做的缺点就是在下单检查库存时会产生少量的if else语句,要解决这个问题,使得逻辑更加灵活,可以借鉴业务规则引擎的做法,把检查库存的前提条件抽象出来,灵活配置业务条件以及对应的业务操作。