继续检查
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/