北国之春

对象的歌唱

常用链接

统计

我的豆瓣

最新评论

面向对象软件开发的敏捷过程(四) 完

继续检查 rental 类中函数 getCharge() 的语句 switch (getMovie().getPriceCode()) ,它提示我们应该将计算 charge 的职责交给 movie 类来完成。这是租借天数作为参数传给 movie 类的相关函数进行计算。我们在 Movie 类添加函数如下:

double getCharge(int dayRented){

              double result=0.0;

              switch(this.getPriceCode()){

              case CHILDRENS:

                     result+=1.5;

                     if(dayRented>3){

                            result+=(dayRented-3)*1.5;

                     }

                     break;

              case REGULAR:

                     result+=2;

                     if(dayRented>2){

                            result+=(dayRented-2)*1.5;

                     }

                     break;

              case NEW_RELEASE:

                     result=dayRented*3;

                     break;

              }

              return result;

       }

原来的 rental 类中 getcharge ()函数中原来的函数体全部注释掉,只添加一行对 movie 类中新添函数的委托调用。 return movie.getCharge(this.dayRented);

继续运行测试,通过了,我们可以放心大胆的删除注释掉的函数体。

movie 类中添加函数

public int getFrequentCount(int dayRented) {

 

              if (getPriceCode() == Movie.NEW_RELEASE && dayRented > 1)

                     return 2;

              else

                     return 1;

       }

在原来的 rental 类中 getFrequentCount ()函数中原有的函数体全部注释掉,添加对 movie 类中新添函数的委托调用。

return movie.getFrequentCount(this.getDayRented());

继续运行测试,防止我们在更改时破坏了任何东西。

最后,由于 movie 的类型是相对比较容易发生变化的,我们必须把具体类型的变化封装出来。这也是封装变化的要求。下面我们可以有两种解决方案:

解决方案一:

movie 的多个类型的计算可以通过继承共同的基类型通过多态完成具体类型的差异运算。

这种解决方法无法解决影片在运行期解决所属类型的需要。如随着时间推移,新片超过一定时间后,会按照普通片或者儿童片进行租费收取。完成此功能我们将借助状态模式的解决方案。

解决方案二:

这里影片的计费策略委托给定价类的具体子类来实现。为了简单起见,我们不引入独立的工厂,把简单工厂的职责交给定价类进行实现。

在类 movie 的构造函数中,我们将类别代码使用 set 函数赋值,以下是更改后的构造函数:

public Movie(String title, int priceCode) {

             

              this.title = title;

              setPriceCode(priceCode);

       }

加入新的类体系如下:

abstract public class Price {

       abstract int getPriceCode();

}

public class NewReleasePrice extends Price {

 

       @Override

       int getPriceCode() {

              // TODO Auto-generated method stub

              return Movie.NEW_RELEASE;

       }

 

}

public class RegularPrice extends Price {

 

       @Override

       int getPriceCode() {

              // TODO Auto-generated method stub

              return Movie.REGULAR;

       }

 

}

public class ChildrenPrice extends Price {

 

       @Override

       int getPriceCode() {

              // TODO Auto-generated method stub

              return Movie.CHILDRENS;

       }

 

}

movie 类中调用新的类体系:

private int priceCode; 改为 private Price price; 使用新的对象委托。

将原来对应的 set/get 函数修改为:

public int getPriceCode() {

              return price.getPriceCode();

       }

       public void setPriceCode(int priceCode) {

              switch(priceCode){

              case REGULAR:

                     price=new RegularPrice();

                     break;

              case CHILDRENS:

                     price=new ChildrenPrice();

                     break;

              case NEW_RELEASE:

                     price=new NewReleasePrice();

                     break;

              }

       }

然后我们把 movie 类中的 getcharge ()函数移动到 price 类中

price 类中添加函数:

double getCharge(int dayRented){

              double result=0.0;

              switch(getPriceCode()){

              case Movie.CHILDRENS:

                     result+=1.5;

                     if(dayRented>3){

                            result+=(dayRented-3)*1.5;

                     }

                     break;

              case Movie.REGULAR:

                     result+=2;

                     if(dayRented>2){

                            result+=(dayRented-2)*1.5;

                     }

                     break;

              case Movie.NEW_RELEASE:

                     result=dayRented*3;

                     break;

              }

              return result;

       }

movie 类中 getcharge ()函数的函数体改为委托 price 的相应操作

return price.getCharge(dayRented);

接下来,把相应的类型分支语句用多态进行替换

RegularPrice

@Override

       double getCharge(int dayRented) {

              // TODO Auto-generated method stub

              double result=2;

              if (dayRented>2){

                     result+=(dayRented-2)*1.5;

              }

              return result;

       }

在类 ChildrenPrice 中:

@Override

       double getCharge(int dayRented) {

              // TODO Auto-generated method stub

              double result=1.5;

              if (dayRented>3){

                     result+=(dayRented-3)*1.5;

              }

              return result;

       }

在类 NewReleasePrice

@Override

       double getCharge(int dayRented) {

              // TODO Auto-generated method stub

              return 3*dayRented;

       }

把基类中的对应函数声明为抽象,使编译器必须调用子类型的对应重载函数实现。

abstract double getCharge(int dayRented);

测试通过。

接下来把类 movie 中函数 getFrequentCount 移动到类 price

public int getFrequentCount(int dayRented) {

      

              if (getPriceCode() == Movie.NEW_RELEASE && dayRented > 1)

                     return 2;

              else

                     return 1;

       }

原有的函数体中更改为委托调用。

return price.getFrequentCount(dayRented);

然后将 price getFrequentCount 的函数体改写为

public int getFrequentCount(int dayRented) {

                            return 1; 

       }

在类 NewReleasePrice 中重载实现

@Override

       public int getFrequentCount(int dayRented) {

              // TODO Auto-generated method stub

              if (dayRented>1)

                     return 2;

              else

              return super.getFrequentCount(dayRented);

       }

引入状态模式以后,我们如果想修改定价方法或是添加新的类型都会变得很容易,例如每种影片的每日租金和最短附加期限等发生变化时只需要更改对应的子类,其余的模块和类都不需改变。

功能扩展:

使用重构功能更改业务逻辑后,我们可以使用对象 / 关系数据库映射框架灵活的把对象存储到数据库中。这方面可以使用 HIBERNATE IBATIS 等关系数据库持久化框架。

 

限于篇幅,具体实现就不展示了。

结论:

应用敏捷软件开发过程和灵活的面向对象设计思想,软件的功能可以稳定快速的扩充。自动化可回归的测试套件,保证了软件修改前后功能的一致性。运用有效、小步的重构结合测试,设计可以逐步优化,并且有目的的应用设计模式,为后期维护提供了有效的办法。

 

参考资料:

《设计模式:可复用面向对象软件的基础 GoF

《重构 - 改善现有代码的设计》 martin fowler

《敏捷软件开发:原则模式和实践》 robert martin

java 和模式》阎宏

《软件复用 - 结构过程和组织》 韩柯

《设计之道》 张逸

oo 开发进阶系列》 方春旭 胡协刚

Head First Design Patterns eric freeman etc

Thinking In Java Bruce.Eckel

非程序员网站 http://www.umlchina.com/

软件测试网 http://www.51testing.com/

 

posted on 2006-08-18 16:40 amigojava 阅读(303) 评论(0)  编辑  收藏


只有注册用户登录后才能发表评论。


网站导航: