里氏替换原则:LSP (Liskov Substitution Principle),如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。
通俗的定义:所有引用基类的地方必须能透明地使用其子类的对象。
以浇水为例。人,拿到工具【水管、水桶、瓶子】,装水后都可以浇水。【水管、桶、瓶子】都可以获取水。应该有个loadWater方法。有watering 浇水功能。人浇水,人只关注浇水。拿到工具就浇水,不用考虑浇水的细节。流程是,人拿工具,用拿到的工具浇水。
类图如下:
代码如下:
Tools 抽象类:
package dim.LSP.simples;
public abstract class Tools {
/**
* 装水
*/
public void loadWater() {
}
/**
* 浇水
*/
public void watering() {
}
}
package dim.LSP.simples;
public class Bottle extends Tools{
@Override
public void loadWater() {
// TODO Auto-generated method stub
System.out.println("Bottle load water");
}
@Override
public void watering() {
// TODO Auto-generated method stub
System.out.println("bottle watering");
}
}
package dim.LSP.simples;
public class WaterPipe extends Tools{
@Override
public void loadWater() {
// TODO Auto-generated method stub
System.out.println("pipe load water");
}
@Override
public void watering() {
// TODO Auto-generated method stub
System.out.println("pipe watering");
}
}
package dim.LSP.simples;
public class Bucket extends Tools{
@Override
public void loadWater() {
// TODO Auto-generated method stub
System.out.println("bucket load water");
}
@Override
public void watering() {
// TODO Auto-generated method stub
System.out.println("bucket watering");
}
}
package dim.LSP.simples;
public class Planter {
Tools tool=null;
public Planter() {
// TODO Auto-generated constructor stub
}
public void setTool(Tools tool)
{
this.tool=tool;
}
public void waterPlant()
{
tool.loadWater();
tool.watering();
}
}
package dim.LSP.simples;
public class TestClass {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
<span style="color:#3333ff;"><strong> Planter planter=new Planter();
//用瓶子浇水
planter.setTool(new Bottle());
planter.waterPlant();
//用水管浇水
planter.setTool(new WaterPipe());
planter.waterPlant();
</strong></span>
}
}
Bottle load water
bottle watering
pipe load water
pipe watering
看测试类代码,浇水的人,拿到工具就浇水。planter 里面:
public void setTool(Tools tool)
{
this.tool=tool;
}
public void waterPlant()
{
tool.loadWater();
tool.watering();
}
Planter planter=new Planter(); //用瓶子浇水 planter.setTool(new Bottle()); planter.waterPlant(); //用水管浇水 planter.setTool(new WaterPipe()); planter.waterPlant();
实现子类对象用父类对象替换。父类能出现的地方,子类就可以出现。也就是概念中的,引用基类的地方必须能透明地使用子类对象。
但是这里有个问题,水管,怎么还要装水。水管直接可以浇水。怎么处理比较合适?可以把水管独立出来,独立为直接浇水的工具,做个单独的抽象类。
类图如下:
package dim.LSP.simples;
public abstract class DirectTools {
}
DirectTools类,可扩展:
package dim.LSP.simples;
public abstract class DirectTools {
}
DirectWaterPipe 代码:
package dim.LSP.simples;
public class DirectWaterPipe extends DirectTools {
Tools tool=new Tools() {
@Override
public void watering() {
// TODO Auto-generated method stub
System.out.println("watering directly");
}
@Override
public void loadWater() {
// TODO Auto-generated method stub
}
};
public Tools getTools()
{
return tool;
}
}
测试类:
package dim.LSP.simples;
public class TestClass {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Planter planter=new Planter();
//用瓶子浇水
planter.setTool(new Bottle());
planter.waterPlant();
//用水管浇水
planter.setTool(new WaterPipe());
planter.waterPlant();
<span style="color:#3333ff;"><strong> //用水管直接浇水
planter.setTool(new DirectWaterPipe().getTools());
planter.waterPlant();</strong></span>
}
}
Bottle load water
bottle watering
pipe load water
pipe watering
watering directly
也可以把DirectWaterPipe 类直接继承Tools,重写loadWater 方法,里面什么也不做。这样有点变扭。
例2:
以视图View为例。View 可以是Button,TextView等等。View 有获取ID,设置ID,监听click等方法。把Button 的对象传给父类View 的对象。
类图如下:
代码如下:
View 抽象类:
package dim.LSP.simples.view;
public abstract class View {
/**
* set the id of view
* @return
*/
public int getId() {
return 0;
}
/**
* get the id of view
* @param id
*/
public void setId(int id) {
}
/**
* listener
*/
public void onClickListener() {
}
}
package dim.LSP.simples.view;
public class Button extends View{
int btnId=0;
@Override
public int getId() {
// TODO Auto-generated method stub
return btnId;
}
@Override
public void setId(int id) {
// TODO Auto-generated method stub
this.btnId=id;
}
@Override
public void onClickListener() {
// TODO Auto-generated method stub
System.out.println("click button now");
}
}
package dim.LSP.simples.view;
public class TextView extends View{
private int textVid=0;
@Override
public int getId() {
// TODO Auto-generated method stub
return textVid;
}
@Override
public void setId(int id) {
// TODO Auto-generated method stub
this.textVid=id;
}
@Override
public void onClickListener() {
// TODO Auto-generated method stub
System.out.println("click textView now ");
}
}
package dim.LSP.simples.view;
public class Activity {
public int getId(View v)
{
return v.getId();
}
public void click(View v)
{
System.out.println("view Id is "+v.getId());
v.onClickListener();
}
}
package dim.LSP.simples.view;
public class TestClass {
public static void main(String[] args) {
Activity activity=new Activity();
//设置button ID,按一下,button
View btn=new Button();
btn.setId(111);
activity.click(btn);
//设置TextView id ,按一下TextView
View textView=new TextView();
textView.setId(888);
activity.click(textView);
}
}
view Id is 111
click button now
view Id is 888
click textView now
上面的类都做了简单的抽象,如果不用抽象类会如何?
类图如下:
使用者,每次用新工具时,都要,调用loadWater 和watering 。每次用新工具都要修改Planter类。不知道会不会抓狂。抽象了之后,可以屏蔽很多细节。
里氏替换原则为良好的继承定义了一个规范,定义包括4层含义:
这里可能会有疑问,为什么不把View和Tools设为接口。感兴趣可以看看这篇文章:接口与抽象类的区别
有所不足、多多指正、共同进步!
相关链接:设计模式六大原则之单一职责原则
参考资料:
《设计模式之禅》
《HeadFirst》
《StartUML详解》
原文地址:http://blog.csdn.net/androidolblog/article/details/45063585