• 面向对象七大设计原则


    面向对象的设计原则概述

    这些原则蕴含在很多设计模式中,它们是从许多设计方案中总结出的指导性原则

    在设计模式的学习中,大家经常会看到诸如“XXX模式符合XXX原则”、“XXX模式违反了XXX原则”这样的语句

    面向对象七大设计原则

    开闭原则

    对修改关闭,对扩展开放。一切都是为了保证代码的扩展性和复用性。而开闭原则是基础要求

    白话文来说就是:软件系统中包含的各种组件,例如模块(Modules)、类(Classes)以及功能(Functions)等等,应该在不修改现有代码的基础上,去扩展新功能,开闭原则中原有“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中“闭”,是指对于代码的修改是封闭的,即不应该修改原有的代码。

    当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

    • 水果接口

    public interface Fruit{
        Double getFriutPrice(String fruitName);
        Double getFruitSize(String fruitName);
    }
    • 水果接口的实现类 ---> 橘子

    public class Peach implments Fruit{
        private String productArea;
        private Double price;
        private Double size;
        ... 构造方法 set get ...
    }
    • 今年橘子想做促销,把橘子的价格调低销售

      • 如果我们直接去修改橘子类的的价格是不适合的

      • 我们可以用一个新的促销类来继承Peach

      • 重写其price方法,这样就保证了原来调用Peach的地方不受影响。

      • 无须修改现有类库的源代码 ,实现了价格的打折处理

    public class SalesPeach extends Peach{
        private static final Double sales_Factor = 0.75d;
        public Peach(Double price,Double size,String productArea) {
            super(price,size,productArea);
        }
        //促销打7.5折
        public Double getPrice(){
            return super.getPrice() * sales_Factor;
        }
    }

    单一职责原则

    一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。 它用于控制类的粒度大小

    接口隔离原则

    使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口

    当一个接口太大时,我们需要将它分割成一些更细小的接口,一个客户端不应该依赖它用不到的接口,使用该接口的客户端仅需知道与之相关的方法即可。每一个接口应该承担一种相对独立的角色,不干不该干的事,该干的事都要干。

    • 动物接口

    public interface Animal {
        void eat();
        void fly();
        void swim();
    }
    • 动物接口实现类 --> 鱼类

    public class Fish implements Animal {
        void eat(){
            System.out.print("鱼儿进食");
        }
        void fly(){}
        void swim(){
            System.out.print("鱼儿游泳");
        } 
    }
    • 动物接口实现类 --> 鸟类

    public class Bird implements Animal { 
        void eat(){
            System.out.print("鸟儿进食");
        }
        void fly(){
            System.out.print("鸟儿飞翔");
        }
        void swim(){}
    }
    • 动物接口实现类 --> 猪类

    public class Pig implements Animal {
        void eat(){
            System.out.print("猪儿进食");
        }
        void fly(){}
        void swim(){}
    }

    上面这种接口和类的关系就看得出来,重接口带来的问题很明显

    • 鱼不会飞

    • 鸟不会游泳

    • 猪不会飞也不会游泳

    但是,由于他们实现的接口是一个重接口,包含了动物的很多行为方法,所以每一个动物的实现者都要重写很多自己根本不需要的行为方法,即使为空

    此时改进的方法就是,将重借口的功能拆分,根据更高的复用性作为一个标准来拆分,如果以上图为列

    • 动物重接口拆分为三个细接口,分别是 eat 、fly、swim

    • 鱼想要吃饭和游泳,就去实现eat 和swim,而不需要去实现fly

    • 同理,猪只会吃饭,也就只需要实现eat即可

    依赖倒置原则

    依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。

    • 大学生选课,该学生特别热爱java课程,所以就选了java课程,当他还想选择大数据课程的时候,我们就需要在Student中增加一个方法 void chooseBidDataCourse();这样是非常不稳定的,这样的修改风险也较高。

    • 改造重构

    public interface Course {
        void choose();
    }
    • Java课程

    public class JavaCourse implements Course {
        void choose() {
            System.out.println("选择了java课程");
        }
    }
    • 大数据课程

    public class BigDataCourse implements Course {
        void choose() {
            System.out.println("选择了大数据课程。");
        }
    }
    • 大学生选课

      • 你想选什么课,都只需要调用chooseCource方法即可

      • 方法的参数是一个层次很高的接口:Course

      • 只要是Course的实现子类或者孙子类都可

    public class Student{
        void chooseCource(Course course) {
            course.choose();
        }
    }

    里式替换原则

    如何去编写继承类的代码,子类不要去覆盖父类已经实现的方法。(抽象模板方法)

    程序中可以使用父类的地方,一定也可以使用子类,即子类可以替换掉父类而不影响程序的正常运行。

    里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在 程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对 象。

    • 父类:BaseClass

    • 子类:SubClass

    • 如果一个方法void test1(BaseClass base)

      • 该test1方法也可以接受SubClass 的入参

    • 如果一个方法void test2(SubClass sub)

      • 该test2方法不可以接受BaseClass 的入参,层次等级不够

    迪米特法则

    最少认知原则,只和你的朋友交谈,不要和陌生人说话。

    只和你的朋友交谈,不要和陌生人说话,对于一个对象,其朋友包括以下几类:除此之外统一称为陌生人

    • 当前对象本身(this);

    • 以参数形式传入到当前对象方法中的对象;

    • 当前对象的成员对象;

    • 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;

    • 当前对象所创建的对象

    迪米特法则要求我们在设计系统时,应该尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用。简言之,就是通过引入一个合理的第三者来降低现有对象之间的耦合度。

    比如:项目经理想知道公司产品的测试结果

    • 项目经理直接去访问与他没有直接关系的测试人员,关系复杂,人员依赖调用复杂,耗时1天

    • 项目经理向测试经理要测试结果,测试经理轻车熟路的调动人员分析得到测试结果,耗时2小时

    明显看出,直接问测试经理要结果,省时省力

     

    合成复用原则

    能用组合关系的情况下,不要使用继承关系。就比如说,如果你想拥有某个对象的功能,不要直接继承它,而是将它作为我的成员变量去使用 ,使用关联复用来取代继承复用

    • 获取数据库连接工具类

    public class DBConnection{
        public  String getConnection(){
            System.out.println("使用MYSQL数据库创建连接");
            return "Mysql";
        }
    }
    • Dao完成数据库操作

    public class UserDAO extends DBConnection {
     
        public void queryUser() {
            String connection = super.getConnection();
            System.out.println("使用数据库连接进行查询" + connection);
        }
    }
    • 加入此时我们要更换Mysql为Oracle了怎么办啊

      • 难道要在DBConnection中新增连接Oracle的方法

      • 然后去所有Dao中更改以往的代码,获取Oracle的的连接吗

      • 无论修改DBConnection还是修改UserDAO都是违背开闭原则的

    • 更改如下

    public class OracleConnection extends DBConnection {
        public  String getConnection(){
            System.out.println("使用Oralce数据库创建连接");
            return "Oracle";
        }
    }
    public class UserDAO  {
        public void queryUser(DBConnection connection ) {
            String connection = connection.getConnection();
            System.out.println("使用数据库连接进行查询" + connection);
        }
    }
    • 根据里氏代换原则,OracleConnection作为子类对象可以覆盖父类DBConnection对象

    • 如果以后,我们还想扩展其他的连接,只需要再写一个子类实现DBConnection,然后注入到方法中即可

    • 灵活地增加新的数据库连接方式

    .

  • 相关阅读:
    [python] 类组合与聚合关系
    [python] 伪私有属性,防止变量名冲突
    [vim] 配置文件之常用命令模式
    [VIM] 编辑器---多行注释和取消注释及多行复制和黏贴
    [Visual Studio Code] 执行python
    [C] 编译器codeblocks安装注意
    字符串全排列
    集合全量子集提取
    random函数详解
    Solr常用命令总结
  • 原文地址:https://www.cnblogs.com/msi-chen/p/14486007.html
Copyright © 2020-2023  润新知