zoukankan      html  css  js  c++  java
  • 设计原则

    什么是设计模式

    • 设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

    为什么要学习设计模式

    • 看懂源代码:如果你不懂设计模式去看Jdk、Spring、SpringMVC、IO等等等等的源码,你会很迷茫,你会寸步难行
    • 看看前辈的代码:你去个公司难道都是新项目让你接手?很有可能是接盘的,前辈的开发难道不用设计模式?
    • 编写自己的理想中的好代码:我个人反正是这样的,对于我自己开发的项目我会很认真,我对他比对我女朋友还好,把项目当成自己的儿子一样

    设计模式分类

    • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
    • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
    • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    设计模式的六大原则

    开放封闭原则(open close Principle)

    • 原则思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
    • 优点:单一原则告诉我们,每个类都有自己负责的职责,里氏替换原则不能破坏继承关系的体系。

    里氏替换原则(Liskov Substitution Principle)

    • 原则思想:使用的基类可以在任何地方使用继承的子类,完美的替换基类
    • 描述:子类可以扩展父类的功能,但不能改变父类原有的 功能。子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法,子类中可以增加自己特有的方法。
    • 优点:增加程序的健壮性,即使增加了子类,原有的子类还可以继续运行,互不影响。

      1、子类可以实现父类的抽象方法,但不能覆盖父类的抽象方法,父类中已经实现好的方法,实际上是在设定一系列规范和契约,虽然它不强求所有子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。举例:

        

          public class C {
            public int func(int a, int b){
              return a+b;
              }
          }

          public class C1 extends C{
            @Override
            public int func(int a, int b) {
              return a-b;
            }
          }

          public class Client{
            public static void main(String[] args) {
              C c = new C1();
              System.out.println("2+1=" + c.func(2, 1));
            }
          }

    运行结果:2+1=1

      上面的运行结果明显是错误的。类C1继承C,后来需要增加新功能,类C1并没有新写一个方法,而是直接重写了父类C的func方法,违背里氏替换原则,引用父类的地方并不能透明的使用子类的对象,导致运行结果出错。

    2、子类可以有自己的个性

      在继承父类属性和方法的同时,每个子类也都可以有自己的个性,在父类的基础上扩展自己的功能。前面其实已经提到,当功能扩展时,子类尽量不要重写父类的方法,而是另写一个方法,所以对上面的代码加以更改,使其符合里氏替换原则,代码如下:

      

        public class C {
          public int func(int a, int b){
            return a+b;
            }
        }

        public class C1 extends C{
          public int func2(int a, int b) {
            return a-b;
          }
        }

        public class Client{
          public static void main(String[] args) {
            C1 c = new C1();
            System.out.println("2-1=" + c.func2(2, 1));
          }
        }

    运行结果:2-1=1

    3、覆盖或实现父类的方法时输入参数可以被放大

    当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松,通过代码来讲解一下

        public class ParentClazz {
          public void say(CharSequence str) {
            System.out.println("parent execute say " + str);
          }
        }

        

        public class ChildClazz extends ParentClazz {
          public void say(String str) {
            System.out.println("child execute say " + str);
          }
        }

        /**
        * 测试
        */
        public class Main {
          public static void main(String[] args) {
            ArrayList list = new ArrayList();
            ParentClazz parent = new ParentClazz();
            parent.say("hello");
            ChildClazz child = new ChildClazz();
            child.say("hello");

          }
        }

        // 执行结果:
        // parent execute say hello
        // child execute say hello

    以上代码中我们并没有重写父类的方法,只是重载了同名方法,具体的区别是:子类的参数 String 实现了父类的参数 CharSequence。此时执行了子类方法,在实际开发中,通常这不是我们希望的,父类一般是抽象类,子类才是具体的实现类,如果在方法调用时传递一个实现的子类可能就会产生非预期的结果,引起逻辑错误,根据里氏替换的子类的输入参数要宽于或者等于父类的输入参数,我们可以修改父类参数为String,子类采用更宽松的 CharSequence,如果你想让子类的方法运行,就必须覆写父类的方法。代码如下:

      public class ParentClazz {
        public void say(String str) {
          System.out.println("parent execute say " + str);
        }
      }

      public class ChildClazz extends ParentClazz {
        public void say(CharSequence str) {
          System.out.println("child execute say " + str);
        }
      }

      public class Main {
        public static void main(String[] args) {
          ParentClazz parent = new ParentClazz();
          parent.say("hello");
          ChildClazz child = new ChildClazz();
          child.say("hello");
        }
      }

    // 执行结果:
    // parent execute say hello
    // parent execute say hello

    4、覆盖或实现父类的方法时输出结果可以被缩小

    当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。代码实现案例如下:

      public abstract class Father {
        public abstract Map hello();
      }

      public class Son extends Father {
        @Override
        public Map hello() {
          HashMap map = new HashMap();
          System.out.println("son execute");
          return map;
        }
      }

      public class Main {
        public static void main(String[] args) {
        Father father = new Son();
        father.hello();
        }
      }

    // 执行结果:
    // son execute

    依赖倒转原则(Dependence Inversion Principle)

    • 依赖倒置原则的核心思想是面向接口编程
    • 依赖倒置原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象类
    • 这个是开放封闭原则的基础,具体内容是:对接口编程,依赖于抽象而不依赖于具体。
    • 撒旦发射点发射点

      1、每个类尽量都有接口或者抽象类,或者抽象类和接口两都具备

      2、变量的表面类型尽量是接口或者抽象类

      3、任何类都不应该从具体类派生

      4、尽量不要覆写基类的方法 
      如果基类是一个抽象类,而这个方法已经实现了,子类尽量不要覆写。类间依赖的是抽象,覆写了抽象方法,对依赖的稳定性会有一定的影响。

      5、结合里氏替换原则使用 
      里氏替换原则:父类出现的地方子类就能出现。结合本章我们得出了一个通俗的规则:接口负责定义public属性和方法,并且声明与其他对象的依赖关系。抽象类负责公共构造部分的实现,实现类准确的实现业务逻辑,同时在适当的时候对父类进行细化。

        public interface IReader{
          //阅读者
          void Read(IRead read);
        }

        public interface IRead{
          //被阅读物
          void Read();

        }

        //文学经典类
        public class LiteraryClassic : IRead{
          //阅读文学经典
          public void Read(){
            Console.WriteLine("阅读文学经典,滋润内心心灵!");
          }
        }

        //小说类
        public class Novel : IRead{
          //阅读小说
          public void Read(){
            Console.WriteLine("阅读小说,放松一下!");
          }
        }

      //小明类
      public class XiaoMing : IReader{
        //阅读
        public void Read(IRead read){
          read.Read();
        }
      }

      static void Main(string[] args){
      XiaoMing xiaoming = new XiaoMing();//实例化对象小明

      IRead literaryClassic = new LiteraryClassic();
      //小明阅读文学经典
      xiaoming.Read(literaryClassic);

      IRead novel = new Novel();
      //小明阅读小说
      xiaoming.Read(novel);
      }

    接口隔离原则(Interface Segregation Principle)

    • 这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了生计和维护方便。所以上文中多次出现:降低依赖,降低耦合。

    迪米特法则(最少知道原则)(Demeter Principle)

      定义

      迪米特法则(Law of Demeter, LoD)是1987年秋天由lan holland在美国东北大学一个叫做迪米特的项目设计提出的,它要求一个对象应该对其他对象有最少的了解,所以迪米特法则又叫做最少知识原则(Least Knowledge Principle, LKP)。

    单一职责原则(Principle of single responsibility)

      意义

      迪米特法则的意义在于降低类之间的耦合。由于每个对象尽量减少对其他对象的了解,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。值得一提的是,这一法则却不仅仅局限于计算机领域,在其他领域也同样适用。比如,美国人就在航天系统的设计中采用这一法则。

    • 原则思想:一个方法只负责一件事情。
    • 描述:单一职责原则很简单,一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序。 这是常识,几乎所有程序员都会遵循这个原则。
    • 优点:降低类和类的耦合,提高可读性,增加可维护性和可拓展性,降低可变性的风险。

      public class ParentClazz {
        public void say(String str) {
          System.out.println("parent execute say " + str);
        }
      }

      public class ChildClazz extends ParentClazz {
        public void say(CharSequence str) {
          System.out.println("child execute say " + str);
        }
      }

      public class Main {
        public static void main(String[] args) {
          ParentClazz parent = new ParentClazz();
          parent.say("hello");
          ChildClazz child = new ChildClazz();
          child.say("hello");
        }
      }

    // 执行结果:
    // parent execute say hello
    // parent execute say hello

  • 相关阅读:
    unittest_assert断言(4)
    unittest_skip跳过用例执行(3)
    unittest_TestSuite测试套件(2)
    unittest_认识unittest(1)
    Selenium_POM架构(17)
    【转】HTML基本代码
    cursor 鼠标样式的几种样式
    【笔记】在json-lib中如何不序列化某些字段
    关于Ext的一些使用心得
    GIT
  • 原文地址:https://www.cnblogs.com/lanblogs/p/15165762.html
Copyright © 2011-2022 走看看