面向对象设计基本原则

六大基本原则

面向设计主要分为六大基本原则:

  • 单一职责原则——SRP
  • 开闭原则——OCP
  • 里式替换原则——LSP
  • 依赖倒置原则——DIP
  • 接口隔离原则——ISP
  • 迪米特原则——LOD

1、单一职责原则

  一职责原则的英文是 Single Responsibility Principle,缩写为 SRP。这个原则的英文描述是这样的:A class or module should have a single reponsibility。如果我们把它翻译成中文,那就是:一个类或者模块只负责完成一个职责(或者功能)

如何理解单一职责原则(SRP)?
  一个类只负责完成一个职责或者功能。不要设计大而全的类,要设计粒度小、功能单一的类。单一职责原则是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性。

如何判断类的职责是否足够单一?
  不同的应用场景、不同阶段的需求背景、不同的业务层面,对同一个类的职责是否单一,可能会有不同的判定结果。实际上,一些侧面的判断指标更具有指导意义和可执行性,比如,出现下面这些情况就有可能说明这类的设计不满足单一职责原则:

  • 类中的代码行数、函数或者属性过多;
  • 类依赖的其他类过多,或者依赖类的其他类过多;
  • 私有方法过多;
  • 比较难给类起一个合适的名字;
  • 类中大量的方法都是集中操作类中的某几个属性。

类的职责是否设计得越单一越好?
  单一职责原则通过避免设计大而全的类,避免将不相关的功能耦合在一起,来提高类的内聚性。同时,类职责单一,类依赖的和被依赖的其他类也会变少,减少了代码的耦合性,以此来实现代码的高内聚、低耦合。但是,如果拆分得过细,实际上会适得其反,反倒会降低内聚性,也会影响代码的可维护性。

2、对扩展开放、修改关闭

  开闭原则的英文全称是 Open Closed Principle,简写为 OCP。它的英文描述是:software entities (modules, classes, functions, etc.) should be open for extension ,but closed for modification。我们把它翻译成中文就是:软件实体(模块、类、方法等)应该“对扩展开放、对修改关闭”。

  用一个通知类说明一下:

package com.alibaba.study.utils;

/**
 * @Auther: wjj
 * @Date: 2020/11/22 12:24
 * @Description:
 */
public class Alert {
    
    private AlertRule rule;
    
    private Notification notification;

    public Alert(AlertRule rule, Notification notification){
        this.rule = rule;this.notification = notification;
    }

    public void check(String api, long requestCount, long errorCount, long durationOfSeconds){
        long tps = requestCount / durationOfSeconds;
        if (tps > rule.getMatchedRule(api).getMaxTps()) {
            notification.notify(NotificationEmergencyLevel.URGENCY, "...");
        }if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {
            notification.notify(NotificationEmergencyLevel.SEVERE, "...");    }
    }
}

  如果上述代码需要新增一个超时时间告警,那么需要修改在check方法中新增参数,并且新增超时判断。代码该动量大,不符合“对扩展开放、对修改关闭”原则。如果对每一种告警方式封装成handler类,每次新增告警方式时,只要新增handler类,不需要修改原代码,代码上线风险减小,可维护性极大增强。

public class Alert {

    private List<AlertHandler> alertHandlers = new ArrayList<>();
    
    public void addAlertHandler(AlertHandler alertHandler){
        this.alertHandlers.add(alertHandler);  
    }
    
    public void check(ApiStatInfo apiStatInfo){
        for (AlertHandler handler : alertHandlers) {      
            handler.check(apiStatInfo);    
        }  
    }
}

public class ApiStatInfo{
    // 省略 constructor/getter/setter 方法
    private String api;
    privatelong requestCount;
    privatelong errorCount;
    privatelong durationOfSeconds;
}

public class ErrorAlertHandler extends AlertHandler{
    publicErrorAlertHandler(AlertRule rule, Notification notification){
        super(rule, notification);
    }
    
    @Override
    publicvoidcheck(ApiStatInfo apiStatInfo){
        if (apiStatInfo.getErrorCount() > rule.getMatchedRule(apiStatInfo.getApi()) {
            notification.notify(NotificationEmergencyLevel.SEVERE, "...");
        }
    }
}

如何理解“对扩展开放、对修改关闭”?
  添加一个新的功能,应该是通过在已有代码基础上扩展代码(新增模块、类、方法、属性等),而非修改已有代码(修改模块、类、方法、属性等)的方式来完成。关于定义,我们有两点要注意。第一点是,开闭原则并不是说完全杜绝修改,而是以最小的修改代码的代价来完成新功能的开发。第二点是,同样的代码改动,在粗代码粒度下,可能被认定为“修改”;在细代码粒度下,可能又被认定为“扩展”。

如何做到“对扩展开放、修改关闭”?
  我们要时刻具备扩展意识、抽象意识、封装意识。在写代码的时候,我们要多花点时间思考一下,这段代码未来可能有哪些需求变更,如何设计代码结构,事先留好扩展点,以便在未来需求变更的时候,在不改动代码整体结构、做到最小代码改动的情况下,将新的代码灵活地插入到扩展点上。

3、里式替换原则

  子类对象(object ofsubtype/derived class)能够替换程序(program)中父类对象(object of base/parentclass)出现的任何地方,并且保证原来程序的逻辑行为(behavior)不变及正确性不被破坏。

  里式替换原则跟多态看起来确实有点类似,但实际上它们完全是两回事。多态是面向对象编程的一大特性,也是面向对象编程语言的一种语法。它是一种代码实现的思路。而里式替换是一种设计原则,是用来指导继承关系中子类的设计要保证在替换父类的时候,不改变原有程序的逻辑以及不破坏原有程序的正确性。

哪些代码明显违背了 LSP?

  • 子类违背父类声明要实现的功能
  • 子类违背父类对输入、输出、异常的约定
  • 子类违背父类注释中所罗列的任何特殊说明

4、接口隔离原则

  接口隔离原则(Interface Segregation Principle, ISP)表明客户端不应该被强迫实现一些他们不会使用的接口,应该把胖接口中的方法分组,然后用多个接口替代它,每个接口服务于一个子模块。简单地说,就是使用多个专门的接口比使用单个接口要好很多。

  • 一个类对另外一个类的依赖性应当是建立在最小的接口上的。
  • 客户端程序不应该依赖它不需要的接口方法(功能)。

接口隔离原则与单一职责原则的区别
  单一职责原则针对的是模块、类、接口的设计。接口隔离原则相对于单一职责原则,一方面更侧重于接口的设计,另一方面它的思考角度也是不同的。接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。

5、依赖倒置原则

  依赖反转原则。依赖反转原则的英文翻译是 Dependency Inversion Principle,缩写为 DIP。中文翻译有时候也叫依赖倒置原则。主要内容为高层模块(high-level modules)不要依赖低层模块(low-level)。高层模块和低层模块应该通过抽象(abstractions)来互相依赖。除此之外,抽象(abstractions)不要依赖具体实现细节(details),具体实现细节(details)依赖抽象(abstractions)。所谓高层模块和低层模块的划分,简单来说就是,在调用链上,调用者属于高层,被调用者属于低层。在平时的业务代码开发中,高层模块依赖底层模块是没有任何问题的。

6、迪米特法则

什么是高内聚?
  所谓高内聚,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一个类中。相近的功能往往会被同时修改,放到同一个类中,修改会比较集中,代码容易维护。

什么是松耦合?
  所谓松耦合是说,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动不会或者很少导致依赖类的代码改动。

如何理解迪米特法则?
不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值