浅谈软件设计原则

通用原则 OCP(开闭原则)

架构设计的主导原则。设计良好的软件应该易于扩展,同时抗拒修改。这是我们进行架构设计的主导原则,其它的原则都为这条原则服务。

USB接口满足OCP原则,各个厂商可以扩展接口实现,但是不能修改接口定义

 正交性设计

“正交性”是从几何学中借来的术语。如果两条直线相交成直角,它们就是正交的。比如图中的坐标轴。用向量术语说,这两条直线互不依赖。沿着一条直线移动,你投影到另一条直线上的位置不变。

在计算技术中,该术语用于表示某种不相依赖性或是解耦性。如果两个或更多事物中的一个发生变化,不会影响其他事物,这些事物就是正交的。在设计良好的系统中,数据库代码与用户界面是正交的:你可以改动界面,而不影响数据库;更换数据库,而不用改动界面。提高生产效率,假定X组件能做M件事情,Y组件能做N件事情。如果他们是正交的,组合起来可以做M*N件事。

 下面给个示例,Foo和Bar接口是正交的,App类与Foo和Bar接口也是正交,它们之间互不影响

public interface Foo {
    void doSomethingX();
}

public interface Bar {
    void doSomethingY();
}

public class App {
    private Foo foo;
    private Bar bar;

    public App(Foo foo, Bar bar) {
        this.foo = foo;
        this.bar = bar;
    }

    /**
     * foo接口变化,不会影响到bar接口,也不会影响到app的实现。
     */
    public void doRightThing(){
        foo.doSomethingX();
        bar.doSomethingY();
    }
}

tips:设计功能的时候,功能之间需要正交;设计数据库字段的时候,字段之间需要正交,禁止多维度的含义映射到一个字段,每个字段含义应该在一个维度上。

高内聚,低耦合  

 抽象原则

抽象原则倡导通过精简和概括来简化实体:精简指的是删除不必要的细节,而概括指的是找出并定义通用的重要特征。

 正例:linux中一切都是文件。

封装原则

封装原则倡导通过隐藏抽象的实现细节和隐藏变化的等方法实现关注点分离和信息隐藏。

暴露出去的信息只是冰山一角

 模块化

模块化原则倡导通过集中和分解等手法创建内聚且耦合松散的抽象。

 正例:ElasticSearch模块化

层次结构

层次结构原则倡导通过分类,概况,替换,排序等方法以层次方式组织抽象。

 正例:TCP/IP协议分层

代码设计原则

SRP(单一职责原则)

任何一个软件模块,都应该有且只有一个被修改的原因,“被修改的原因“指系统的用户或所有者,翻译一下就是,任何模块只对一个用户的价值负责。该原则指导我们如何拆分组件。

LSP(里氏替换原则)

当用同一接口的不同实现互相替换时,系统的行为应该保持不变。该原则指导的是接口与其实现方式。

ISP(接口隔离原则)

不依赖任何不需要的方法、类或组件。该原则指导我们的接口设计。

当我们依赖一个接口但只用到了其中的部分方法时,其实我们已经依赖了不需要的方法或类,当这些方法或类有变更时,会引起我们类的重新编译,或者引起我们组件的重新部署,这些都是不必要的。所以我们最好定义个小接口,把用到的方法拆出来。

DIP(依赖反转原则)跨越组件边界的依赖方向永远与控制流的方向相反。该原则指导我们设计组件间依赖的方向。

依赖反转原则是个可操作性非常强的原则,当你要修改组件间的依赖方向时,将需要进行组件间通信的类抽象为接口,接口放在边界的哪边,依赖就指向哪边。

 组件构建原则

无依赖环原则

健康的依赖应该是个有向无环图(DAG),互相依赖的组件,实际上组成了一个大组件,这些组件要一起发布、一起做单元测试。我们可以通过依赖反转原则 DIP 来解除依赖环。

 

稳定依赖原则

依赖必须要指向更稳定的方向。

这里组件的稳定性指的是它的变更成本,和它变更的频繁度没有直接的关联(变更的频繁程度与需求的稳定性更加相关)。影响组件的变更成本的因素有很多,比如组件的代码量大小、复杂度、清晰度等等,最最重要的因素是依赖它的组件数量,让组件难以修改的一个最直接的办法就是让很多其他组件依赖于它!

组件稳定性的定量化衡量指标是:不稳定性(I) = 出向依赖数量 / (入向依赖数量 + 出向依赖数量)。如果发现违反稳定依赖原则的地方,解决的办法也是通过 DIP 来反转依赖。

 

稳定抽象原则

一个组件的抽象化程度应该与其稳定性保持一致。为了防止高阶架构设计和高阶策略难以修改,通常抽象出稳定的接口、抽象类为单独的组件,让具体实现的组件依赖于接口组件,这样它的稳定性就不会影响它的扩展性。

组件抽象化程度的定量化描述是:抽象程度(A)= 组件中抽象类和接口的数量 / 组件中类的数量。

将不稳定性(I)作为横轴,抽象程度(A)作为纵轴,那么最稳定、只包含抽象类和接口的组件应该位于左上角(0,1),最不稳定、只包含具体实现类,没有任何接口的组件应该位于右下角(1,0),他们连线就是主序列线,位于线上的组件,他们的稳定性和抽象程度相匹配,是设计良好的组件。位于(0,0)周围区域的组件,它们是非常稳定(注意这里的稳定指的是变更成本)并且非常具体的组件,因为他们的抽象程度低,决定了他们经常改动的命运,但是又有许多其他组件依赖他们,改起来非常痛苦,所以这个区域叫做痛苦区。右上角区域的组件,没有其他组件依赖他们,他们自身的抽象程度又很高,很有可能是陈年的老代码,所以这个区域叫做无用区。

 

 

 

 

转自:

https://blog.csdn.net/csujiangyu/article/details/104017063

 

posted @ 2023-07-21 08:23  starof  阅读(28)  评论(0编辑  收藏  举报