1.定义

      1.1标准定义:(Liskov Substitution Principle)LSP可表述为在软件中能够使用基类对象,那么也一定能够使用其子类对象。也就是说子类一定是基类,但是基类就不一定是子类了。使用LSP时需要注意几个问题:

            (1)子类所有方法必须在父类中声明,或者子类必须实现父类中声明的所有方法。为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,父类中不提供相应的声明,则无法再父类对象中直接使用该方法。如果在子类中声明了新的方法,在父类中没有这个方法,那么客户端针对父类编程无法使用子类中新增方法。

            (2)在运用里氏代换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口。这实际就是开闭原则。

      1.2与开闭原则比较:里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

2.案例

      某系统需要实现对重要数据的加密处理,在数据操作类(DataOperator)中需要调用加密类中定义的加密算法,系统提供加密两个的加密类CipherA和CipherB,它们实现不同的加密方法,在Dataoperator中可以选择其中的一个实现加密操作,如下图所示:

     

3.分析

      在本实例中,导致系统灵活性和可扩展性差的本质原因是MainClass和数据操作类(Dataoperator)都针对每一个具体类进行编程,每增加一个具体类都将修改源代码。此时可以将CipherB作为CipherA的子类。根据里氏代换原则所有能够接受CipherA类对象的地方都可以接受CipherB类的对象,因此可以简化操作类和MainClass(客户端类)的代码,而且将CipherA对象替换成CipherB类对象很方便,无需修改任何源代码。如果需要增加一个新的加密类,如CipherC,只需要将CipherC类作为CipherA类和CipherB类的子类即。重构后的内图如下所示:

     

4.设计

 

      4.1XMLHelper类:主要是从xml文件里获取到类名  

package LiskovSubstitution;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/*
 * 这个类主要是为了从配置文件获取加密类的类名
 * 
 * 
 * */
public class XMLHepler {

    public XMLHepler() {
         
    }
    public String getClassName()
    {
        DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
        DocumentBuilder builder=null;
        try
        {
            builder=factory.newDocumentBuilder();
        }catch(ParserConfigurationException e)
        {
            e.printStackTrace();
        }
        Document document=null;
        try {
            document=builder.parse("LSP.xml");//配置文件在项目目录下 加载配置文件
        }catch (SAXException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }
        NodeList nList=document.getElementsByTagName("EncryptionClassName");
        return nList.item(0).getFirstChild().getNodeValue();//获取到节点值
    }
}

      4.2CipherA类:加密类的父类。

package LiskovSubstitution;
/*
 * 
 * 加密类:父类 方法在父类中定义
 * */
public class CipherA {

    public CipherA() {
        // TODO Auto-generated constructor stub
    }
    public String encrypt(String plainText)
    {
         
        byte []a=plainText.getBytes();
        for(int i=0;i<a.length;i++)
        {
            a[i]^=8;//对每个字符进行异或操作
            
        }
        String reString=new String(a);
        return reString ;
    }
}

      4.3CipherB类:加密类的子类,一种加密算法。

package LiskovSubstitution;

public class CipherB extends CipherA{

    public CipherB() {
         
    }
    public String encrypt(String plainText)
    {
           //为了看到效果 此处就不再进行加密 验证是否调用了此方法
          return  "这是子类方法";
    }

}

      4.4MainClass类:mian函数,程序入口。

package LiskovSubstitution;

public class MainClass {

    /*
     * 主函数 调用加密算法 并输出
     * */
    private XMLHepler xmlHepler=null;
    private DataOperator operator=null;
    private CipherA cipher=null;//定义一个加密类 
    private String testString="good good study";//测试文本
    public MainClass() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        
        xmlHepler=new XMLHepler();
        operator=new DataOperator();
        cipher = (CipherA)Class.forName(xmlHepler.getClassName()).newInstance();//根据xml读取的类名 来实例化相应的对象
        operator.setCipherA(cipher );
    String result=    operator.encrypt(testString);
        System.out.println(result);
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
         
          new MainClass();
           
    }

}

      4.5DataOperator类:操作类,选择加密类对象。

package LiskovSubstitution;

public class DataOperator {
  
    private  CipherA cipher =null;//加密类
    public DataOperator() {
        
    }
    public void setCipherA(CipherA cipherA)
    {
        this.cipher =cipherA;
    }
    public String encrypt(String plainText)
    {
        return cipher.encrypt(plainText);
    }

}

      4.6运行效果:

     

     


            注:我参考的书是清华大学出版社,由刘伟主编的《设计模式》。代码中存在的不足,还请多多指教。

 

posted on 2018-10-22 20:48  Juice-Dreamer  阅读(1383)  评论(0编辑  收藏  举报