头图
人生没有白走的路,每一步都算数

前言

在面向对象的软件设计中,只有尽量降低各个模块之间的耦合度,才能提高代码的复用率,系统的可维护性、可扩展性才能提高。面向对象的软件设计中,有23种经典的设计模式,是一套前人代码设计经验的总结,如果把设计模式比作武功招式,那么设计原则就好比是内功心法。常用的设计原则有七个,本文将具体介绍单一职责原则。

设计原则简介

  • 单一职责原则:专注降低类的复杂度,实现类要职责单一;
  • 开放关闭原则:所有面向对象原则的核心,设计要对扩展开发,对修改关闭;
  • 里式替换原则:实现开放关闭原则的重要方式之一,设计不要破坏继承关系;
  • 依赖倒置原则:系统抽象化的具体实现,要求面向接口编程,是面向对象设计的主要实现机制之一;
  • 接口隔离原则:要求接口的方法尽量少,接口尽量细化;
  • 迪米特法则:降低系统的耦合度,使一个模块的修改尽量少的影响其他模块,扩展会相对容易;
  • 组合复用原则:在软件设计中,尽量使用组合/聚合而不是继承达到代码复用的目的。

这些设计原则并不说我们一定要遵循他们来进行设计,而是根据我们的实际情况去怎么去选择使用他们,来让我们的程序做的更加的完善。

单一职责原则

就一个类而言,应该仅有一个引起它变化的原因,通俗的说,就是一个类只负责一项职责。此原则的核心就是解耦和增强内聚性。

如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计。

优点

(1)降低类的复杂度;

(2)提高类的可读性,提高系统的可维护性;

(3)降低变更引起的风险(降低对其他功能的影响)。

我们来举一些简单的例子来说明一下这个单一职责原则

public class Animal {

    public void breathe(String animal) {
        System.out.println(animal + "生活在陆地上");
    }
}

public class Test {

    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.breathe("牛");
        animal.breathe("羊");
    }
}

运行结果:

牛生活在陆地上
羊生活在陆地上
动物并不是都生活在陆地上的,鱼就是生活在水中的,修改时如果遵循单一职责原则,需要将Animal类细分为陆生动物类Land,水生动物Water,代码如下:
public class Land {

    public void breathe(String animal) {
        System.out.println(animal + "生活在陆地上");
    }
}

public class Water {
    public void breathe(String animal) {
        System.out.println(animal + "生活在水里");
    }
}

public class Test {
    public static void main(String[] args) {
        Land land = new Land();
        land.breathe("牛");
        land.breathe("马");
        Water water = new Water();
        water.breathe("鱼");
    }
}

运行结果:

牛生活在陆地上
马生活在陆地上
鱼生活在水里
但是问题来了如果这样修改花销是很大的,除了将原来的类分解之外,还需要修改客户端。而直接修改类Animal来达成目的虽然违背了单一职责原则,但花销却小的多,代码如下:
public class Animal {

    public void breathe(String animal) {
        if ("鱼".equals(animal)) {
            System.out.println(animal + "生活在水里");
        }
        System.out.println(animal + "生活在陆地上");
    }
}

public class Test {

    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.breathe("牛");
        animal.breathe("羊");
        animal.breathe("鱼");
    }
}

运行结果:

牛生活在陆地上
马生活在陆地上
鱼生活在水里
可以看到,这种修改方式要简单的多。但是却存在着隐患:有一天需要将鱼分为生活在淡水中的鱼和生活在海水的鱼,则又需要修改Animal类的breathe方法,而对原有代码的修改会对调用“猪”“牛”“羊”等相关功能带来风险。

这种修改方式直接在代码级别上违背了单一职责原则,虽然修改起来最简单,但隐患却是最大的。还有一种修改方式:

public class Animal {

    public void breathe(String animal) {
        System.out.println(animal + "生活在陆地上");
    }

    public void breathe1(String animal) {
        System.out.println(animal + "生活在水里");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.breathe("牛");
        animal.breathe("羊");
        animal.breathe1("鱼");
    }
}

运行结果:

牛生活在陆地上
马生活在陆地上
鱼生活在水里

可以看到,这种修改方式没有改动原来的方法,而是在类中新加了一个方法,这样虽然也违背了单一职责原则,但在方法级别上却是符合单一职责原则的,因为它并没有动原来方法的代码。

总结

以上通过一个应用场景简单介绍了下单一职责原则的使用,上面三种设计,没有最合理,只有最合适。

这三种方式各有优缺点,那么在实际编程中,采用哪一中呢?其实这真的比较难说,需要根据实际情况来确定。

例如本文所举的这个例子,它太简单了,它只有一个方法,所以,无论是在代码级别上违反单一职责原则,还是在方法级别上违反,都不会造成太大的影响。 实际应用中的类都要复杂的多,一旦发生职责扩散而需要修改类时,除非这个类本身非常简单,否则还是遵循单一职责原则的好。

理解单一职责原则,最重要的就是理解职责的划分,职责划分的粒度取决于需求的粒度。


初念初恋
175 声望17 粉丝

引用和评论

0 条评论