愉快的工作又开始了。Leader安排了一个新的任务,给一个酸奶店的点单软件增加新的功能。
这个程序的原来所有的酸奶都继承于奶酪类(Cheese)
public abstract class Cheese {
public String name;
public String introduce(){
return name;
}
public abstract int price();
所有具体的酸奶继承奶酪类并重写价格price方法,以原味酸奶和宫廷酸奶为例:
public class Original extends Cheese{
public Original() {
name="Original";
}
@Override
public int price() {
return 10;
}
}
public class Court extends Cheese{
public Court() {
name="Court";
}
@Override
public int price() {
return 8;
}
}
这样的话直接调用实现类的introduce和price就可以得到名字和价格了。
但是随着酸奶店的规模越来越大,品种也越来越多,出现了一些新的品种的酸奶和配料,比如:草莓,奥利奥,樱桃。这些配料和酸奶进行搭配然后计算总共的价格和名称。
稍微思考一下,可能会有人选择这样写代码,以原味酸奶加草莓为例:
public class OriginalStrawberry extends Cheese{
public Original() {
name="OriginalStrawberry ";
}
@Override
public int price() {
return 10;
}
}
这样看起来,似乎并没有什么不妥,但是仔细想一想,酸奶加配料这样的组合有多少种可能呢?如果每一种可能都要写出来一个类的话,那么简直要类爆炸,并且如果任何一个酸奶或者配料更改了价格的话,那么所有关联类都需要修改,如果顾客想要双倍的草莓呢?或者草莓加樱桃呢?这样写下去简直是反人类的方法。
设计原则之一:类应该对扩展开发,对修改关闭。比如说对于一些我们经过测试没有问题的代码,原则上不应该修改源代码,而应该在源代码基础之上进行扩展。
如果我们这样设计代码是不是更好一点,还是以原味酸奶加草莓为例:我们先把一杯原味酸奶作为主体(被修饰者),用草莓作为修饰者修饰主体。就是先得到一个原味酸奶的对象,用草莓对象修饰它,然后调用price方法将草莓的价钱加上去。
这样的做法可以动态的将奶酪和配料松耦合的搭配在一起。所以先把系统分为奶酪和配料两个部分。
public abstract class Batching extends Cheese {
public abstract String introduce();
}
草莓的实现类如下:
public class Strawberry extends Batching {
Cheese cheese;
public Strawberry(Cheese cheese) {
this.cheese = cheese;
}
@Override
public int price() {
return(cheese.price()+2);
}
@Override
public String introduce() {
return(cheese.introduce()+",Strawberry");
}
}
樱桃的实现类如下:
public class Cherry extends Batching{
Cheese cheese;
public Cherry(Cheese cheese) {
this.cheese = cheese;
}
@Override
public String introduce() {
return cheese.introduce()+"Cherry";
}
@Override
public int price() {
return cheese.price()+3;
}
}
以原味酸奶加草莓为例:
public class Test {
public static void main(String[] a){
Cheese cheese=new Original();
cheese =new Strawberry(cheese);
System.out.println(cheese.introduce()+cheese.price());
}
}
输出结果:Original,Strawberry12