但是现在每当计价策略发生改变,我们必须修改Part 的每个子类!
       一个更好的思路是采用一个PricePolicy类,通过对其进行继承以提供不同的计价策略,那麽这里是在运用设计模式里面的策略模式了,解决起来算是很完美了:
    public class Part {     
           private PricePolicy pricePolicy;     
           public void setPricePolicy(PricePolicy policy) {     
               pricePolicy = policy;     
           }     
           public void setPrice(double price) {     
               pricePolicy.setPrice(price);     
           }     
           public double getPrice() {     
               return pricePolicy.getPrice();     
           }     
    }     
    public class PricePolicy {     
           private double basePrice;     
           public void setPrice(double price) {     
               basePrice = price;     
           }     
           public double getPrice() {     
               return basePrice;     
           }     
    }        
    public class SalePrice extends PricePolicy {     
           private double discount;     
           public void setDiscount(double discount) {     
               this.discount = discount;     
           }     
           public double getPrice() {     
               return basePrice*discount;     
           }        
    }        

看起来我们所做的是将问题推迟到另一个类中,将“变化”封装在PricePolicy类里面。但是使用该解决方案,我们可通过改变Part对象,在运行期间动态地来设定计价的策略。另一个解决方案是使每个ConcretePart从数据库或属性文件中获取其当前的价格,这样相当于把“变化”封装在了属性文件里面了。

三、里氏代换原则(LSP)

(1) 由Barbar Liskov(芭芭拉.里氏)提出,是继承复用的基石。
(2) 严格表达:如果每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换称o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型.
    换言之,一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉出基类对象和子类对象的区别.只有衍生类可以替换基类,软件单位的功能才能不受影响,基类才能真正被复用,而衍生类也能够在基类的基础上增加新功能。
(3) 反过来的代换不成立
(4) <墨子.小取>中说:"白马,马也; 乘白马,乘马也.骊马(黑马),马也;乘骊马,乘马也."
(5) 该类西方的例程为:正方形是否是长方形的子类(答案是"否")。类似的还有椭圆和圆的关系。
(6) 应当尽量从抽象类继承,而不从具体类继承,一般而言,如果有两个具体类A,B有继承关系,那么一个简单的修改方案是建立一个抽象类C,然后让类A和B成为抽象类C的子类.即如果有一个由继承关系形成的登记结构的话,那么在等级结构的树形图上面所有的树叶节点都应当是具体类;而所有的树枝节点都应当是抽象类或者接口.
(7) "基于契约设计(Design By Constract),简称DBC"这项技术对LISKOV代换原则提供了支持.该项技术Bertrand Meyer伯特兰做过详细的介绍:
使用DBC,类的编写者显式地规定针对该类的契约.客户代码的编写者可以通过该契约获悉可以依赖的行为方式.契约是通过每个方法声明的前置条件(preconditions)和后置条件(postconditions)来指定的.要使一个方法得以执行,前置条件必须为真.执行完毕后,该方法要保证后置条件为真.是说,在重新声明派生类中的例程(routine)时,只能使用相等或者更弱的前置条件来替换原始的前置条件,只能使用相等或者更强的后置条件来替换原始的后置条件.

    一个软件实体如果使用的是一个基类的话那么一定适用于其子类,而且它察觉不出基类对象和子类对象的区别。也是说,在软件里面,把基类都替换成它的子类,程序的行为没有变化。

LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
  
下面,我们从代码重构的角度来对LSP进行理解。LSP讲的是基类和子类的关系。只有当这种关系存在时,里氏代换关系才存在。如果两个具体的类A,B之间的关系违反了LSP的设计,(假设是从B到A的继承关系)那么根据具体的情况可以在下面的两种重构方案中选择一种。