设计模式——七大原则

七大原则

设计模式是对软件中普遍存在(反复出现)的各种问题提出的统一解决方案.设计模式是对程序猿设计程序的一种规范,能够提高程序的可用性,可拓展性,稳定性,规范性以及可阅读性.
设计模式有七大原则,这七个原则相当于是对设计模式的一种规范

单一职责原则

定义

单一职责原则指的是一个类只能执行一个职责.假设一个类执行了两个职责,当第一个职责发生变动时,肯可能会导致第二个职责执行错误.

示例

class Vehicle{
    /**
     * 直接调用交通工具的该方法会有些许不妥:因为该方法只是说明了公路上的交通工具,违反了单一职责原则
     * @param vehicle
     */
    public void run(String vehicle){

        System.out.println(vehicle + "在公路上运行...");
    }
}
public class SingleResponsibility1 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("自行车");
        vehicle.run("汽车");
        vehicle.run("飞机");
        vehicle.run("轮船");
    }
}

/*
运行结果:
自行车在公路上运行...
汽车在公路上运行...
飞机在公路上运行...
轮船在公路上运行...

*/

上述代码中的Vehicle类违背了单一职责原则.改进作法是将Vehicle类分成多个细化的交通工具类

class RoadVehicle{
    public void run(String vehicle){
        System.out.println(vehicle + "在公路上运行");
    }
}
class AirVehicle{
    public void run(String vehicle){
        System.out.println(vehicle + "在天空运行");
    }
}
class WaterVehicle{
    public void run(String vehicle){
        System.out.println(vehicle + "在水中运行");
    }
}


上述代码遵守了单一职责原则,但因为对于代码改动较大,对于用户而言不太友好,而且因为类中方法个数比较少,所以可以进一步将单一职责原则的作用对象从类转变到方法上

class Vehicle{
    public void runRoad(String vehicle){
        System.out.println(vehicle + "在公路上运行...");
    }
    public void runAir(String vehicle){
        System.out.println(vehicle + "在天空上运行...");
    }
    public void runWater(String vehicle){
        System.out.println(vehicle + "在水中上运行...");
    }
}

特点

  1. 单一职责原则能够降低类的复杂度
  2. 单一职责原则能够提高类的可读性和可维护性
  3. 单一职责原则能够降低类变更带来的风险
  4. 一般情况下,单一职责原则的单位是类,但当一个类中的逻辑足够简单时可以违背单一职责原则,当类中的方法较少时,可以将单一职责原则作用到方法层面上

接口隔离原则

定义

接口隔离原则指的是当一个类通过接口依赖于另一个类时应该建立在最小的接口的基础上.

示例

在这里插入图片描述
如图,类A和类C分别通过依赖接口1来实现依赖类B和类D.但类A只是通过func1,2,3方法来依赖类B,类C只是通过func1,3,5方法来依赖于类D.而类B和类D在实现接口1时则需要将5个方法全部重写,违背了接口隔离原则.
改进措施是将接口1进行细化成三个子接口,分别实现方法1和3,2和4,5.如图
在这里插入图片描述

interface Interface1 {
    void func1();

    void func2();

    void func3();

    void func4();

    void func5();
}

/**
 * B类实现了接口1
 */
class B implements Interface1 {
    @Override
    public void func1() {
        System.out.println("B 实现了 func1");
    }

    @Override
    public void func2() {
        System.out.println("B 实现了 func2");
    }

    @Override
    public void func3() {
        System.out.println("B 实现了 func3");
    }

    @Override
    public void func4() {
        System.out.println("B 实现了 func4");
    }

    @Override
    public void func5() {
        System.out.println("B 实现了 func5");
    }
}

/**
 * D实现了接口1
 */
class D implements Interface1 {
    @Override
    public void func1() {
        System.out.println("D 实现了 func1");
    }

    @Override
    public void func2() {
        System.out.println("D 实现了 func2");
    }

    @Override
    public void func3() {
        System.out.println("D 实现了 func3");
    }

    @Override
    public void func4() {
        System.out.println("D 实现了 func4");
    }

    @Override
    public void func5() {
        System.out.println("D 实现了 func5");
    }
}

/**
 * A类依赖于类B,且只使用接口1中的方法1,2,3
 */
class A {
    public void depend1(Interface1 i) {
        i.func1();
    }

    public void depend2(Interface1 i) {
        i.func2();
    }

    public void depend3(Interface1 i) {
        i.func3();
    }
}

/**
 * C类依赖于类D,且只使用接口1中的方法1,3,5
 * <p>
 * 上述这种接口的设计不满足接口隔离原则,即一个类对另一个类的依赖没有建立在最小的接口上,应该将接口进行分解
 */
class C {
    public void depend1(Interface1 i) {
        i.func1();
    }

    public void depend5(Interface1 i) {
        i.func5();
    }

    public void depend3(Interface1 i) {
        i.func3();
    }
}

// ===================改进前后========================
interface Interface2 {
    void func1();

    void func3();


}
interface Interface3{

    void func5();
}
interface Interface4{
    void func2();
    void func4();


}

/**
 * B类实现了接口1
 */
class B1 implements Interface2,Interface3 {
    @Override
    public void func1() {
        System.out.println("B 实现了 func1");
    }

    @Override
    public void func5() {
        System.out.println("B 实现了 func5");
    }

    @Override
    public void func3() {
        System.out.println("B 实现了 func3");
    }

}

/**
 * D实现了接口1
 */
class D1 implements Interface3,Interface4 {

    @Override
    public void func2() {
        System.out.println("D 实现了 func2");
    }

    @Override
    public void func4() {
        System.out.println("D 实现了 func4");
    }
    @Override
    public void func5() {
        System.out.println("D 实现了 func5");
    }

}

/**
 * A类依赖于类B,且只使用接口1中的方法1,2,3
 */
class A1{
    public void depend1(Interface2 i){
        i.func1();
    }
    public void depend2(Interface4 i){
        i.func2();
    }
    public void depend3(Interface2 i){
        i.func3();
    }
}

/**
 * C类依赖于类D,且只使用接口1中的方法1,3,5
 *
 * 通过将1个接口分解成3个接口,类B和D在实现接口时就无须像之前一样实现一个接口中的全部方法
 * 符合了接口隔离原则
 */
class C1{
    public void depend1(Interface2 i){
        i.func1();
    }
    public void depend5(Interface3 i){
        i.func5();
    }
    public void depend3(Interface2 i){
        i.func3();
    }
}

依赖倒转原则

定义

依赖倒转的规则有5条

  1. 高级模块不依赖于低级模块.低级模块和高级模块应该都依赖于抽象(接口或抽象类)
  2. 抽象不应该依赖于细节,细节应该依赖于抽象
  3. 依赖倒转原则的核心是面向接口编程
  4. 依赖倒转原则的设计理念是相对于细节的多变性,抽象的东西要稳定的多.
  5. 使用接口和抽象类来设计规范

示例

class Person{
	public void receive(Emain email){
		System,out,println(email.getInfo());
	}
}
class Email{
	public String getInfo(){
		return "电子邮件";
	}
}

上述代码中用具体的实现类Email来当做Person类中receive方法的参数(即用Email类作为Person类的一个依赖)来输出email信息.但当程序进行拓展时,如添加了新的接收类,如WeChat等,则需要再次提供receive方法,违背了依赖倒转原则
改进措施是依赖于抽象.通过创建MyReceiver接口作为依赖,而具体的接收类则负责实现接口

interface MyReceiver{
    String getInfo();
}
class Email implements MyReceiver{
    @Override
    public String getInfo() {
        return "电子邮件信息: hello world";
    }
}
class WeChat implements MyReceiver{
    @Override
    public String getInfo() {
        return "微信消息: Hello";
    }
}

class Person{
    /**
     * 高级模块和底层模块依赖抽象(接口形参)
     */
    public void receive(MyReceiver receiver){
        System.out.println(receiver.getInfo());
    }
}

里氏替换原则

定义

里氏替换原则实际上是对继承的一种说明.

  1. 父类在类中实现具体的方法时,更多的体现的是一种设计规范,尽管父类没有强制要求子类遵守这种规范,但如果子类对父类的方法进行任意修改则会破坏该继承体系
  2. 尽管继承可以提高程序的复用性和效率,但处于继承关系中的父类和子类会造成程序间的耦合性增强,父类代码的改动很可能导致子类代码出现故障
    因此里氏替换原则在描述继承时,要求子类尽可能的不要重写父类中的方法;可以使用组合,聚合,依赖的方式来代替继承的关系

示例

public class Liskov1 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println("11-3=" + a.func1(11,3));

        B b = new B();
        System.out.println("12-5=" + b.func2(12,5));
    }
}
class A{
    public int func1(int num1,int num2){
        return num1 - num2;
    }
}
class B extends A{
    @Override
    public int func1(int num1, int num2) {
        return num1 + num2;
    }
    public int func2(int num1,int num2){
        return func1(num1,num2);
    }
}

上述代码违背了里氏替换原则,即子类对父类中的方法进行了重写而导致子类本意是调用父类的方法时误调用成了自己重写的方法,改进措施就是利用其他关系来代替组合

class Base{

}
class A1 extends Base{
    public int func1(int num1,int num2){
        return num1 - num2;
    }
}
class B1 extends Base{
    private A1 a1 = new A1();
    public int func2(int num1,int num2){
        return a1.func1(num1,num2);
    }
}

迪米特原则

定义

迪米特原则也被称为最少知道原则.它描述的是一个对象应该对其他对象保持最少的了解.因为类与类关系越密切,耦合性就越大.
迪米特原则的一个核心要点在于只与直接朋友进行通信而不与间接朋友进行通信.直接朋友指的是一个对象在另一个对象的成员变量,方法的形参和返回值等位置出现时的关系,而间接朋友指的是位于局部变量等位置的关系.

开闭原则

定义

开闭原则是设计模式七大原则中最重要,最核心的原则.
开闭原则的核心在于当对程序进行扩展时,应该尽可能的通过增加逻辑来扩展(开),而不是通过修改原有的逻辑来扩展(关).用抽象来构建框架,有实现来扩展细节

合成复用原则

定义

合成复用原则的核心在于尽量使用组合/聚合的方式来关联类,尽量避免使用继承的方式来关联类


版权声明:本文为weixin_52477733原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。