设计模式六大原则之里氏替换原则

前端之家收集整理的这篇文章主要介绍了设计模式六大原则之里氏替换原则前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

一、概念:

里氏替换原则:LSP (Liskov Substitution Principle),如果对每一个类型为T1的对象o1,都有类型为T2o2,使得以定义的所有程序P在所有的对象o1都换成时,程序的行为没有变化,那么类型是类型的子类型。

通俗的定义:所有引用基类的地方必须能透明地使用其子类的对象。

二、例子:

以浇水为例。人,拿到工具【水管、水桶、瓶子】,装水后都可以浇水。【水管、桶、瓶子】都可以获取水。应该有个loadWater方法。有watering 浇水功能。人浇水,人只关注浇水。拿到工具就浇水,不用考虑浇水的细节。流程是,人拿工具,用拿到的工具浇水。

类图如下:



代码如下:

Tools 抽象类:

package dim.LSP.simples;

public abstract class Tools {

	/**
	 * 装水
	 */
	public void loadWater() {
	}
	/**
	 * 浇水
	 */
	public void watering() {
	}
}

Bottle瓶子也可以是浇水工具,继承工具类Tools
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");
	}

}


waterPipe类:
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");
	}

}

Bucket类:
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() {
	}
}

Button类,继承View类:

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");
	}

}

TextView类:

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 ");
	}

}

Activity类:

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类。不知道会不会抓狂。抽象了之后,可以屏蔽很多细节。

@H_319_403@

三、4层含义:

里氏替换原则为良好的继承定义了一个规范,定义包括4层含义:


这里可能会有疑问,为什么不把View和Tools设为接口。感兴趣可以看看这篇文章接口与抽象类的区别


有所不足、多多指正、共同进步!


相关链接设计模式六大原则之单一职责原则


参考资料:

接口与抽象类的区别

《设计模式之禅》

《HeadFirst》

《StartUML详解》

设计模式六大原则

设计模式之六大原则

猜你在找的设计模式相关文章