• 面向对象设计原则快速理解



    面向对象设计原则有7个,但有的地方把常用的前5个简称为五大原则

    单一职责原则

    概念

    一个类应该只有一个引起它变化的原因,不要让一个类拥有多种变化的理由。即一个类只应该完成一个职责相关的业务,不要让一个类承担过多的职责。粒度大小根据业务来,即简单的职责可以让一个类兼任。复杂的职责必须独立

    说人话

    一个类、方法、接口,最好只干一件事

    举例
    定义一个people类,用来规定人类的基本行为,如:走、跑、跳、蹲等。但不能把开车、弹钢琴、射击等个性化行为全部加到people类中,因为这些不是所有人都会。当实例化people对象是一个儿童时,这些方法就用不到。接口和类是类似的。

    定义一个drive方法,用来开车。但是车有很多种,驾驶方法也不一样,drive方法需要根据传入的参数,判断车型,然后执行不同的驾驶方式。所以我们最好把每种车的驾驶单独写一个方法,这样修改某种车的驾驶方法时,不会影响其他驾驶方法的代码。

    接口隔离原则

    概念

    客户端只依赖于它所需要的接口;它需要什么接口就提供什么接口,把不需要的接口剔除掉。类间的依赖关系应建立在最小的接口上。

    说人话

    其实还是单一职责原则的体现,只不过是特指接口的。就是在设计接口时,尽量细化,不要在一个接口里设置大量方法。这会导致实现接口的类,不得不实现一些自己用不到方法,增加代码维护难度

    开放-封闭原则

    概念

    软件实现应该对扩展开放,对修改关闭

    说人话

    设计程序时,对于之后新增和修改需求,应该保证不修改底层代码,而是扩展底层代码,只在调用层做修改

    开闭原则是设计原则的核心原则,其他设计原则都是开闭原则的体现和补充
    软件系统是否有良好的接口(抽象)设计是判断软件系统是否满足开闭原则的一种重要的判断基准。现在多把开闭原则等同于面向接口的软件设计

    举例

    //定义一个商品接口
    interface product{
    	function getName();
    	function getPrice();
    }
    //手机、电脑类实现了该接口
    class phone implements product{
    	public function getName{ echo '我是手机';}
    	public function getPrice{ echo '我的价格是1000';}
    }
    class pc implements product{
    	public function getName{ echo '我是pc';}
    	public function getPrice{ echo '我的价格是5000';}
    }
    //调用层
    $phone = new phone();
    $phone->getName;
    $phone->getPrice;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    此时,若pc需要获取打折价格,如果按照常规思维,我么可能会有2种修改方式。
    方式1,直接在接口中新增一个获取折扣价的方法,然后在pc类里实现这个方法。但是这样不仅修改了底层逻辑代码,不符合开闭原则;同时phone类也需要实现折扣方法,但phone并不需要折扣。
    方式2,直接修改pc类的getPrice方法,获取折扣价。但这样还是违背了开闭原则,同时导致pc类无法获取原来的价格

    正确的方法是,新建一个discountPc类,继承pc类后,重载getPrice方法,如下

    class discountPc extends pc{
    	public function getPrice(){
    		echo '我的折扣价是4000';
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这样,获取原价就用pc对象,获取折扣价就用discountPc对象。只需要在调用层修改代码即可。

    但问题又来了,商品接口需要增加一个获取规格的方法getSku,所有实现类都会用到,而且getSku和原有功能没有任何关系,无法使用重载,那么代码该如何扩展呢。
    显然,按上面底层逻辑代码的结构,是无法满足在开闭原则下进行扩展的。我们需要把接口粒度调整到最小,即只实现一个功能,如下

    interface productInfo{
    	function execute();
    }
    class pcName implements productInfo{ 
    	public function execute(){
    		echo '我是pc';
    	}
    }
    class pcPrice implements productInfo{ 
    	public function execute(){
    		echo '我的价格是5000';
    	}
    }
    class phoneName implements productInfo{...}
    class phonePrice implements productInfo{...}
    //调用层
    public getProductInfo(productInfo $info){
    	$info->execute();
    }
    getProductInfo(new pcName());
    getProductInfo(new pcPrice());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这样的话,若需要新增需求或者修改需求,只需新增类,继承productInfo,然后在调用层调用即可。
    不过这样也会导致接口的约束性变差,代码量增大,这就需要在设计程序架构时,根据需求进行取舍

    里氏替换原则

    概念

    里氏替换原则强调的是设计和实现要依赖于抽象而非具体;子类只能去扩展基类,而不是隐藏或者覆盖基类。所有引用基类的地方必须能透明地使用其派生类的对象。

    说人话

    应用程序中任何父类对象出现的地方,我们都可以用其子类的对象来替换,并且可以保证原有程序的逻辑行为和正确性。

    里氏替换原则要求子类不要覆盖父类的方法,如果实在要覆盖,需要遵守以下规则

    • 1、 前置条件不能被加强
      前置条件即输入参数是不能被加强的,比如父类方法没有限制输入参数必须为int,而子类内的方法限制了传入参数为int,此时在调用处替换父类对象为子类对象就可能引发异常。
      也就是说,子类对输入的数据的校验比父类更加严格,那子类的设计就违背了里式替换原则。

    • 2、后置条件不能被削弱
      后置条件即输出,假设我们的父类方法约定输出参数要大于0,调用父类方法的程序根据约定对输出参数进行了大于0的验证。而子类在实现的时候却输出了小于等于0的值。此时子类的涉及就违背了里氏替换原则

    • 3、不能违背对异常的约定
      在父类中,某个函数约定,只会抛出 ArgumentNullException 异常, 那子类的设计实现中只允许抛出 ArgumentNullException 异常,任何其他异常的抛出,都会导致子类违背里式替换原则。

    依赖倒置原则

    概念

    低耦合就是依赖倒置原则。高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。中心思想是面向接口编程

    说人话

    面向接口编程,类最好都有接口限定他们的行为,方便统一调用

    举例
    最直接的应用就是工厂模式。工厂负责根据条件生成并返回一个对象,调用端可以直接使用返回对象中的方法,而不需判断对象是否不同,对象是否有这个方法。这是因为工厂对象,遵循了依赖倒置原则,都实现了同一个接口中的方法

    迪米特法则

    概念

    迪米特法则(Law of Demeter )又叫做最少知识原则。如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用。一个对象应当对其他对象尽可能少的了解

    说人话

    迪米特法则主要强调两点
    1、 在一个类中,可以直接调用的类,只能是当前类的属性,或者方法的传入值或传出值(即在方法中生成了一个类)
    2、调用外部类时,外部类暴露的方法越少越好

    迪米特法则主要是为了降低类之间的耦合
    第1点,是为了可以在外部控制依赖类
    第2点是为了减少外部调用类的方法过多,导致耦合度过高

    合成复用原则

    概念

    合成复用原则(又叫组合/聚合复用原则)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现

    说人话

    不要为了复用代码,随便使用继承

    举例
    一个订单类A,一个数据库类B
    A需要连接数据库,操作数据,为了复用B的功能,继承了B。这显然是不合理的,而且当A需要使用一个新的数据库类C时,A的代码就需要改变,未被了开闭原则

    依赖注入就是合成复用原则和迪米特法则的具体是想,把要用到的数据库类实例传入A中,A无论想用B还是C都可以在调用层修改即可

    A 水果,B 苹果 ,B是A的一种,就属于继承关系(is a)
    A人 ,B运动,A和B都有跑步功能,但A和B不是同一种类,属于聚合关系(has a)

  • 相关阅读:
    m基于中继协助的认知无线电频谱切换机制的matlab仿真分析
    计算机网络性能指标
    pytest--fixture的使用(前置、后置)
    今天给在家介绍一篇基于jsp的旅游网站设计与实现
    基于springboot实现家具销售电商平台管理系统项目【项目源码+论文说明】计算机毕业设计
    每日一练:LeeCode-56、合并区间【数组+滑动窗口】
    2023 Google 开发者大会:Web平台新动向
    1.计算机系统概述-王道考研计算机组成原理
    第1章 算法和数据结构
    百度地图——鹰眼轨迹服务
  • 原文地址:https://blog.csdn.net/u012830303/article/details/126830306