• 里氏代换原则


    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运行效果:

         

         


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

  • 相关阅读:
    转:C++ 智能指针的正确使用方式
    C/C++各个周期的学习
    转: 工作中用的C++库
    转:【软件设计】深入理解日志系统的意义
    初级爬虫第四天
    初级爬虫第三天
    初级爬虫第二天
    初级爬虫第一天
    pep8介绍
    MySQL训练营01
  • 原文地址:https://www.cnblogs.com/Juice-Dreamer/p/9832419.html
Copyright © 2020-2023  润新知