里氏代换原则在Java中的体现
前面说了里氏代换原则,那么现在说一下里氏代换原则在Java中的使用。
里氏代换原则要求父类类型可以使用的,那么子类一定可以适用。因此子类必须具有基类的全部接口,并且可能更广。如果Java程序破坏了这个条件,那么编译器就会出错。
这里以接口为例:在接口中规定了所有的方法都必须是public,这其实就是一种对里氏代换原则的体现,因为接口被其它类所实现,那么接口就是父类,这个父类的类型如果不是public,那么就无法被其它子类调用,就违反了前面说的父类类型可以使用的,子类一定可以适用的规则。
另外如果一个父类接口Base声明了一个public方法,那么子类B是不能够将这个方法的访问权限public修改为低级的private,因为如果子类B将权限设置为private那么这个方法只能被自己的类调用,而无法被其它类调用,这样就违反了里氏代换原则。
public interface Base {
//默认public
public void method();
}
public class Son implements Base {
@Override
private void method() {
}
}
编译器会报如下错误!
里氏代换原则在设计模式中的体现。
策略模式
其实说到里氏代换原则首先就会想到策略模式,因为策略模式的核心就是封装到基类中的一种实现方式。
前面我也在了解业务时学习了一些策略模式,策略模式讲的是,如果有一组算法,那么就将每一个算法封装起来,使得它们可以互相交换。
上图中可以看到在实现策略模式时,不同的策略类的具体实现都会实现共同的接口,这个接口是为了保证所有的策略类的相关角色都会在接口中确定,这样就保证了所有的角色都在同一类型的结构中。
接口中变量的真实类型则是具体的策略类。即会在接口中返回具体的类型,这个类型就是需要实现的策略类。
/**
* 基类类型
*/
public interface Base {
//默认public
public Object toDo(int type);
}
/**
* 策略的具体实现类
*/
public class StrategyA implements Base{
@Override
public Object toDo(int type) {
System.out.println("策略A的实现");
return type;
}
}
/**
* 策略的具体实现类
*/
public class StrategyB implements Base {
@Override
public Object toDo(int type) {
System.out.println("策略B的实现");
return type;
}
}
/**
* 策略的具体实现类
*/
public class StrategyC implements Base {
@Override
public Object toDo(int type) {
System.out.println("策略C的实现");
return type;
}
}
/**
* 该类返回具体的策略
*/
public class Context {
private Base base;
/**
* 初始化类型,所有的策略都实现了基类Base,即参数中传递哪个策略类型就返回哪个策略类型
* @param base
*/
public Context(Base base){
this.base = base;
}
/**
* 执行策略
* @param type
* @return
*/
public Object excute(int type){
return base.toDo(type);
}
}
public class ExtendsDemo {
public static void main(String[] args) {
//初始化对应的策略,然后调用策略就可以得到对应策略的实现
Context context = new Context(new StrategyA());
//此时执行的就是策略A
context.excute(1);
//执行策略B
Context context1 = new Context(new StrategyB());
context1.excute(2);
//执行策略C
Context context2 = new Context(new StrategyC());
context2.excute(3);
}
}
代理模式
代理模式的核心就是给一个类提供一个代理类,然后去做想做的事情,更简单话理解就是类似于房产中介,我想买房但是自己不想看各种问题,就找房产中介,中介就是代理类。
代理模式在Java中给一个对象提供代理对象,并由代理对象控制原对象的引用。因此代理对象可以获得原对象的所有权做原对象可以做的事情。
从上图中可以看到原对象已经与基类建立了引用关系,现在存在一个代理类代理原对象,那么这个代理类也与基类建立了引用关系,因此代理类也和基类进行了关系绑定。
/**
* 基类类型
*/
public interface Base {
//默认public
public Object toDo(int type);
}
/**
* 原对象与基类建立引用
*/
public class Agent implements Base {
@Override
public Object toDo(int type) {
System.out.println("原对象");
return null;
}
}
/**
* 代理类
*/
public class AgentProxy implements InvocationHandler {
private Object object;
public AgentProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理开始");
Object res = method.invoke(object,args);
System.out.println("代理结束返回结果");
return res;
}
}
public class AgentMain {
public static void main(String[] args) {
Base base = new Agent();
Base proxy = (Base) Proxy.newProxyInstance(
Base.class.getClassLoader(),
new Class[]{Base.class},
new AgentProxy(base));
proxy.toDo(1);
}
}
参考资料:
1、《Java与模式》