面向对象六大基本原则

来源:互联网 发布:淘宝联盟保持高佣金 编辑:程序博客网 时间:2024/05/22 17:06

在学习或者项目开发中,经常会碰到设计原则和设计模式,设计原则相对于设计模式来说更抽象些,贯穿于整个项目架构和项目编程中,是一个灵魂,设计模式的话,通俗一点说就是某个功能模块使用何种方式去实现。

一、单一职责原则
定义:
单一职责原则(SRP:Single responsibility principle)又称单一功能原则,它规定一个类应该只有一个发生变化的原因。

通俗一点就是,一个类或者接口中只能存在功能相似程度比较高的方法,比如要绘制矩形、圆,也要显示矩形和圆,那么可以将绘制矩形和圆的实现定义在一个接口中,将显示矩形和圆的实现定义在另外一个接口中;而不能将绘制和显示定义在同一个接口中。

/** * Created by Administrator on 2017/10/25. * 绘制图形接口 */public interface DrawPicture {    /**     * 绘制矩形     */    void drawCircle();    /**     * 绘制矩形     */    void drawRect();}
/** * Created by Administrator on 2017/10/25. * 显示图形接口 */public interface ShowPicture {    /**     * 显示圆形     */    void showCircle();    /**     * 显示矩形     */    void showRect();}
public class Picture implements DrawPicture,ShowPicture{    @Override    public void drawCircle() {    }    @Override    public void drawRect() {    }    @Override    public void showCircle() {    }    @Override    public void showRect() {    }}

这样子绘制图形或者显示图形有变动时对其他的并没有太多影响。

特点:
1、降低了类或者接口的复杂性,对类或者接口的职责有清晰明确定义;
2、提高了可读性;
3、提高可维护性;
4、降低变更引起的风险,接口改变只影戏相应的实现类,不影响其他类;

注意点:
1、接口一定要做到单一职责;
2、类的单一职责比较难以实现,尽量做到只有一个原因引起变化;
3、一个方法尽可能做一件事,能分解就分解,分解到原子级别;

适用范围:
接口、类、方法

二、里氏替换原则
定义:
所有引用基类的地方必须能透明的使用其子类对象。

需求:
玩家玩射击气球游戏,既能使用AK仿真气枪,也可以使用步枪或其他枪类射击气球。

/** * Created by Administrator on 2017/10/25. * 枪接口 */public interface IGun {    /**     * 射击方法     */    void fire();}
/** * Created by Administrator on 2017/10/25. * 枪的具体实现类 */public class AK47Gun implements IGun{    @Override    public void fire() {        System.out.println("AK47Gun--->开火");    }}
/** * Created by Administrator on 2017/10/25. *  枪的具体实现类 */public class USPGun implements IGun{    @Override    public void fire() {        System.out.println("USPGun--->开火");    }}
/** * Created by Administrator on 2017/10/25. */public class Play {    private IGun iGun;    public Play(IGun iGun){        this.iGun=iGun;    }    /**     * 进攻方法     */    public void fire(){        iGun.fire();    }}
/** * Created by Administrator on 2017/10/25. * 用户类 */public class Client {    public void playFire(){        IGun ak47Gun=new AK47Gun();        Play play=new Play(ak47Gun);        play.fire();        IGun uspGun=new USPGun();        Play play1=new Play(uspGun);        play1.fire();    }}

里氏替换原则的核心是继承,也是多态的体现,同样它的优缺点也是继承的优缺点;
优点:
1、代码共享:子类都拥有父类的方法和属性,将父类的代码共享给子类;
2、重用性:提供了代码的重用性,子类重用父类的代码;
3、扩展性:提高代码的可扩展性,子类可以随意扩展父类;
4、开放性:父类随意扩展,开放性随之增加;
缺点:
1、侵入性:子类强制继承或实现父类的方法和属性
2、灵活性:降低代码的灵活性,子类必须拥有父类的属性和方法,子类收到父类的约束,这是从子类的角度考虑;
3、耦合性:增强了耦合性,父类的属性和方法被修改时,还需要顾及其子类,可能会带来大量的重构,这是从父类考虑;

注意点:
1、父类方法返回值类型F,子类方法返回值类型S,里氏替换原则是S范围必须小于F范围;
2、父类、子类方法参数类型或者数量不同,如果要符合里氏替换原则要求的话,子类参数必须>=父类参数,即不能让子类自己定义的方法被调用;
3、如果想使用里氏替换原则,尽量避免让子类拥有自己单独的成员变量或者方法,如果子类个性多了,子类父类关系很难调和;

三、依赖倒置原则
定义:
高层模块不应该依赖于底层模块,二者都应该依赖于其抽象,抽象应该不依赖于细节,细节应该依赖于抽象。

问题由来:
类A直接依赖类B,假如要将类A改成依赖类C,则必须要通过修改类A的代码来达成;这种场景下,类A一般是高层模块,负责复制的业务逻辑;类B和类C是底层模块,负责基本的原子操作,假如修改类A,会给程序带来不必要的风险。

解决方案:
将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口间接与类B或类C发生联系,则会大大降低修改类A的几率。

需求场景:
母亲给孩子讲故事

/** * Created by Administrator on 2017/10/26. * 读书接口 */public interface Reader {    /**     * 获取读书的类容     * @return  返回读书的类容     */    public String getReadContent();}
/** * Created by Administrator on 2017/10/26. * 读书接口的实现类 */public class NewsReader implements Reader{    @Override    public String getReadContent() {        return "妈妈在读新闻";    }}
/** * Created by Administrator on 2017/10/26. * 读书接口的实现类 */public class StoryBook implements Reader{    @Override    public String getReadContent() {        return "妈妈在读小说故事";    }}
/** * Created by Administrator on 2017/10/26. */public class Monther {    private Reader reader;    public Monther(Reader reader){        this.reader=reader;    }    public void iReader(){        String readContent = reader.getReadContent();        System.out.println("readContent-->"+readContent);    }}
/** * Created by Administrator on 2017/10/26. */public class Client {    public void iReader(){        Reader newsReader=new NewsReader();        Monther monther=new Monther(newsReader);        monther.iReader();        Reader storyBook=new StoryBook();        Monther monther1=new Monther(storyBook);        monther1.iReader();    }}

依赖倒置原则也是策略模式的体现;依赖倒置原则的本质就是通过接口或者抽象类,是各个类或者模块实现彼此独立,实现模块间松耦合。

依赖倒置原则好处:
1、减少了类之间的耦合;
2、提高系统的稳定性;
3、降低并发风险;
4、提高代码的可读性;

依赖倒置注入的实现:
1、构造方法依赖对象:通过构造函数参数声明依赖对象;
2、setter方法依赖对象:通过setter函数参数声明依赖对象;
3、接口注入依赖对象:在接口方法的参数中声明依赖对象;

注意点:
尽量不要覆盖方法,如果方法在抽象类中已经实现,子类不要覆盖。

四、接口隔离原则
定义:
1、建立单一的接口, 功能尽量细化, 不要建立臃肿的接口;
2、不需要的接口 ,客户端尽量不依赖其不需要的接口, 客户端需要什么接口就提供什么接口, 剔除不需要的接口, 对接口进行细化, 保持接口方法最少;
3、最小接口 : 类间的依赖关系应该建立在最小接口上, 细化接口;

接口隔离原则应遵循不能违反单一原则,这里说的不能违反单一原则指的是:接口隔离原则要求建立单一的接口,功能尽量细化,但是不能将业务一致或者相似的功能拆分成多个接口;但是和单一原则又有不同;
单一原则和接口隔离原则区别:
单一原则:注重职责,注重业务逻辑上的划分;
接口隔离原则:注重的是接口的方法尽量少;

特点:
1、接口尽量小:拆分接口,接口隔离的核心定义,不出现臃肿的接口,但是在拆分接口的时候需要注意接口小有限度,不能违反单一原则,不能将一个业务逻辑拆分成两个接口;
2、接口高内聚:提高接口、类、方法、模块的处理能力,减少对外交互,接口中尽量少用public修饰,对外公布的public方法越少,变更的风险就越小,有利于后期的维护;
3、定制服务:系统模块间的耦合需要有相互访问的接口,这里就需要为各个客户端的访问提供定制的服务接口,在提供接口的时候只需要提供访问者需要的接口,不需要的就不提供;
4、接口隔离限度:接口粒度越小,系统越灵活,但是同时使系统结构复杂,开发难度增加,降低了系统的可维护性,

原子接口划分原则:
接口模块对应的关系:一个接口只服务于一个子模块或业务逻辑;
方法压缩:通过业务逻辑,压缩接口中的public方法,减少接口的方法数量;
修改适配:尽量去修饰已经污染的接口,如果变的风险较大,采用适配器模式进行转换处理;

五、迪米特法则
定义:
也叫最少只是原则,一个对象应该对其他对象有最少的了解,即一个类对自己需要耦合或者调用的类知道的最少。

通俗一点就是将类B暴露给类A的方法封装,暴露的越少越好,类B高内聚,类A低耦合;所以在系统设计的时候,能用 private 就用private , 能用 protected 就用 protected, 能少用 public 就少用 public, 能加上 final 就加上 final;

优缺点:
优点:类间解耦, 弱耦合, 耦合降低, 复用率提高;
缺点:类间的耦合性太低, 会产生大量的中转或跳转类, 会导致系统的复杂性提高, 加大维护难度;

六、开闭原则
定义:
软件的实体 类, 模块, 函数 应该对扩展开放, 对修改关闭; 即 软件实体 应该 通过扩展实现变化, 不是通过 修改已有的代码实现变化;

1、利于测试 : 如果改变软件内容, 需要将所有的测试流程都执行一遍, 如 单元测试, 功能测试, 集成测试等, 如果只是扩展, 只单独测试扩展部分即可;
2、提高复用性 : 所有逻辑都从原子逻辑组合, 原子逻辑粒度越小, 复用性越大; 这样避免相同逻辑存在, 修改时需要修改多个此相同逻辑;
3、提高可维护性 : 维护一个类最好的方式是 扩展一个类, 而不是修改一个类, 如果需要修改需要读懂源码才能修改, 扩展的话只需要了解即可, 直接继承扩展;

原创粉丝点击