使用Java语言编写设计模式-3.设计模式七大原则

lingwh原创2022年9月7日大约 10 分钟约 2953 字

3.设计模式七大原则

3.1.章节内容概述

本章节涉及主要内容有:
 3.1.章节内容概述
 3.2.章节内容大纲
 3.3.单一职责原则SRP
 3.4.接口隔离原则ISP
 3.5.依赖倒转原则DIP
 3.6.里氏替换原则LSP
 3.7.开闭原则OCP
 3.8.迪米特原则LoD
 3.9.合成复用原则CARP
具体每个小节中包含的内容可使通过下面的章节内容大纲进行查看。

3.2.章节内容大纲

3.3.单一职责原则SRP

3.3.1.单一职责原则介绍

概述
一个类应该只负责一项职责

为什么要遵循单一职责原则
如类A负责两个不同职责: 职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2。

注意事项
a.降低类的复杂度,一个类只负责一项职责
b.提高类的可读性,可维护性
c.降低变更引起的风险
d.通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则,只有类中方法数量足够少,可以在方法级别保持单一职责原则,但是通常不要违反,因为很难确定代码以后是否会发生更改

3.3.2.单一职责原则代码

3.3.2.1.不使用单一职责原则代码

package com.dragonsoft.designpattern.basic.principles.single_reponsibility;

import org.junit.Test;

/**
 * 不使用单一职能原则
 *      Vehicle1.run()方法违反了单一职能原则
 */
public class SingleReponsibility1 {
    @Test
    public void test(){
        Vehicle1 vehicle1 = new Vehicle1();
        vehicle1.run("汽车");
        vehicle1.run("火车");
        vehicle1.run("飞机");
    }
}
class Vehicle1 {
    public void run(String vehicle) {
        System.out.println(vehicle + "在公路上跑...");
    }
}

3.3.2.2.使用单一职责原则代码

3.3.2.2.1.类级别单一职责原则代码
package com.dragonsoft.designpattern.basic.principles.single_reponsibility;

import org.junit.Test;

/**
 * 类级别的单一职能原则:严格遵守了单一职责原则
 * 遵守了单一职责原则:但是会导致类大爆炸
 */
public class SingleResponsibility2 {
    @Test
    public void run(){
        RoadVehicle roadVehicle = new RoadVehicle();
        roadVehicle.run("汽车");
        AirVehicle airVehicle = new AirVehicle();
        airVehicle.run("飞机");
        WaterVehicle waterVehicle = new WaterVehicle();
        waterVehicle.run("轮船");
    }
}

class RoadVehicle {
    public void run(String vehicle){
        System.out.println(vehicle+"在路上跑...");
    }
}

class WaterVehicle {
    public void run(String vehicle){
        System.out.println(vehicle+"在水里跑...");
    }
}

class AirVehicle {
    public void run(String vehicle){
        System.out.println(vehicle+"在天上飞...");
    }
}

3.3.2.2.2.方法别单一职责原则代码
package com.dragonsoft.designpattern.basic.principles.single_reponsibility;

import org.junit.Test;

/**
 * 方法级别的单一职能原则,会导致类中方法大爆炸
 */
public class SingleResponsibility3 {
    @Test
    public void run(){
        Veicle3 veicle3 = new Veicle3();
        veicle3.runRoad("汽车");
        veicle3.runAir("飞机");
        veicle3.runWater("轮船");
    }
}

class Veicle3 {
    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+"在水里跑...");
    }
}

3.4.接口隔离原则ISP

3.4.1.接口隔离原则介绍

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上

3.4.2.接口隔离原则代码

3.4.2.1.不使用接口隔离原则代码

Interface1.java
package com.dragonsoft.designpattern.basic.principles.interface_segregation;

/**
 * 不遵守接口隔离原则
 */
public interface Interface1 {
    void operator1();
    void operator2();
    void operator3();
    void operator4();
    void operator5();
}

/**
 * A1通过Interface1依赖B类,但只会用到1、2、3方法
 */
class A1 {
    public void depend1(Interface1 interface1){
        interface1.operator1();

        //如果不进行接口隔离,interface1仍然可以调用接口中的operator2()、operator3()、operator4()和operator5()两个方法,实际根本不需要这四个方法
        //interface1.operator2();
        //interface1.operator3();
        //interface1.operator4();
        //interface1.operator5();
    }
    public void depend2(Interface1 interface1){
        interface1.operator2();
        //如果不进行接口隔离,interface1仍然可以调用接口中的operator1()、operator3()、operator4()和operator5()两个方法,实际根本不需要这两个方法
        //interface1.operator1();
        //interface1.operator3();
        //interface1.operator4();
        //interface1.operator5();
    }
    public void depend3(Interface1 interface1){
        interface1.operator3();
        //如果不进行接口隔离,interface1仍然可以调用接口中的operator1()、operator2()、operator4()和operator5()两个方法,实际根本不需要这两个方法
        //interface1.operator1();
        //interface1.operator2();
        //interface1.operator4();
        //interface1.operator5();
    }
}
/**
 * C1通过Interface1依赖D类,但只会用到1、4、5方法
 */
class C1 {
    public void depend1(Interface1 interface1){
        interface1.operator1();

        //如果不进行接口隔离,interface1仍然可以调用接口中的operator2()、operator3()、operator4()和operator5()两个方法,实际根本不需要这四个方法
        //interface1.operator2();
        //interface1.operator3();
        //interface1.operator4();
        //interface1.operator5();
    }
    public void depend4(Interface1 interface1){
        interface1.operator4();

        //如果不进行接口隔离,interface1仍然可以调用接口中的operator1()、operator2()、operator3()和operator5()两个方法,实际根本不需要这四个方法
        //interface1.operator1();
        //interface1.operator2();
        //interface1.operator3();
        //interface1.operator5();
    }
    public void depend5(Interface1 interface1){
        interface1.operator5();

        //如果不进行接口隔离,interface1仍然可以调用接口中的operator1()、operator2()、operator3()和operator4()两个方法,实际根本不需要这四个方法
        //interface1.operator1();
        //interface1.operator2();
        //interface1.operator3();
        //interface1.operator4();
    }
}
class B1 implements Interface1 {
    @Override
    public void operator1() {
        System.out.println("B1实现了操作1...");
    }

    @Override
    public void operator2() {
        System.out.println("B1实现了操作2...");
    }

    @Override
    public void operator3() {
        System.out.println("B1实现了操作3...");
    }

    @Override
    public void operator4() {
        System.out.println("B1实现了操作4...");
    }

    @Override
    public void operator5() {
        System.out.println("B1实现了操作5...");
    }
}
class D1 implements Interface1 {
    @Override
    public void operator1() {
        System.out.println("D1实现了操作1...");
    }

    @Override
    public void operator2() {
        System.out.println("D1实现了操作2...");
    }

    @Override
    public void operator3() {
        System.out.println("D1实现了操作3...");
    }

    @Override
    public void operator4() {
        System.out.println("D1实现了操作4...");
    }

    @Override
    public void operator5() {
        System.out.println("D1实现了操作5...");
    }
}

Interface1Client.java
package com.dragonsoft.designpattern.basic.principles.interface_segregation;

import org.junit.Test;

/**
 * 调用Interface1的客户端
 */
public class Interface1Client {
    /**
     * 测试A1通过Interface1依赖B1,但是只调用1、2、3方法
     */
    @Test
    public void testA1() {
        A1 a1 = new A1();
        //本来要传入接口Interface1的,现在根据面向接口编程原则,传入Interface1的实现B1
        a1.depend1(new B1());
        a1.depend2(new B1());
        a1.depend3(new B1());
    }

    /**
     * 测试C1通过Interface1依赖D1
     */
    @Test
    public void testC1() {
        C1 c1 = new C1();
        //本来要传入接口Interface1的,现在根据面向接口编程原则,传入Interface1的实现D1
        c1.depend1(new D1());
        c1.depend4(new D1());
        c1.depend5(new D1());
    }
}

3.4.2.2.使用接口隔离原则代码

Interface2.java
package com.dragonsoft.designpattern.basic.principles.interface_segregation;

public interface Interface2 {
    void operator1();
}
/**
 * 遵循接口隔离原则
 *      把接口1差分为3个接口
 */
//public interface Interface1 {
//    void operator1();
//    void operator2();
//    void operator3();
//    void operator4();
//    void operator5();
//}
interface Interface2A {
    void operator1();
}

interface Interface2B {
    void operator2();
    void operator3();
}

interface Interface2C {
    void operator4();
    void operator5();
}

/**
 * 客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
 * A2就是客户端
 * A2通过Interface1A,InterfaceC依赖B2类
 * 接口隔离后,interface2A只能调用operator1()
 *           interface2B只能调用operator2()和operator3()
 *           interface2C只能调用operator4()和operator5()
 */
class A2 {
    public void depend1(Interface2A interface2A){
        interface2A.operator1();
    }
    public void depend2(Interface2B interface2B){
        interface2B.operator2();
    }
    public void depend3(Interface2B interface2B){
        interface2B.operator3();
    }
}

/**
 * 客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
 * C2就是客户端
 * C2通过Interface2A,Interface2B依赖D2类
 * 接口隔离后,interface2A只能调用operator1()
 *            interface2B只能调用operator2()和operator3()
 *            interface2C只能调用operator4()和operator5()
 */
class C2 {
    public void depend1(Interface2A interface2A){
        interface2A.operator1();
    }
    public void depend4(Interface2C interface2C){
        interface2C.operator4();
    }
    public void depend5(Interface2C interface2C){
        interface2C.operator5();
    }
}

class B2 implements Interface2A,Interface2B {
    @Override
    public void operator1() {
        System.out.println("B2实现了操作1...");
    }

    @Override
    public void operator2() {
        System.out.println("B2实现了操作2...");
    }

    @Override
    public void operator3() {
        System.out.println("B2实现了操作3...");
    }
}

class D2 implements Interface2A,Interface2C {
    @Override
    public void operator1() {
        System.out.println("D2实现了操作1...");
    }

    @Override
    public void operator4() {
        System.out.println("D2实现了操作4...");
    }

    @Override
    public void operator5() {
        System.out.println("D2实现了操作5...");
    }
}


Interface2Client.java
package com.dragonsoft.designpattern.basic.principles.interface_segregation;

import org.junit.Test;

public class Interface2Client {
    /**
     * 测试A2通过接口Interface2A,Interface2B依赖B2
     */
    @Test
    public void testA2() {
        A2 a2 = new A2();
        a2.depend1(new B2());
        a2.depend2(new B2());
        a2.depend3(new B2());
    }

    /**
     * 测试C2通过接口Interface2B,Interface2C依赖D2
     */
    @Test
    public void testC2() {
        C2 c2 = new C2();
        c2.depend1(new D2());
        c2.depend4(new D2());
        c2.depend5(new D2());
    }
}

3.5.依赖倒转原则DIP

3.5.1.依赖倒转原则介绍

概述
a.高层模块不应该依赖底层模块,二者都应该依赖其抽象
b.抽象不应该依赖细节,细节应该依赖抽象
c.依赖反转的中心思想是面向结构编程
d.依赖反转的思想是基于这样的理念:相对于细节的多变性,抽象的东西要稳定的多,以抽象为基础的架构比以细节为基础的架构要稳定的多,在java中,抽象的多指接口和抽象类,细节就是具体的实现类
e.使用类或者接口的目的是制定规范,而不涉及任何具体的子类操作,把展现细节的任务交给他们的实现类去做

依赖关系传递的三种方式
a.接口传递
b.构造方法传递
c.setter方法传递

注意事项
a.底层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好
b.变量的声明类型尽量都是抽象类或者接口,这样我们的变量引用和实际对象之间,就存在一个缓冲层,有利于程序的优化和扩展
c.继承是遵循里氏替换原则

3.5.2.依赖倒转原则代码

3.5.2.1.不使用依赖倒转原则代码

package com.dragonsoft.designpattern.basic.principles.dependenceinversion;

/**
 * 不使用依赖反转原则
 */
public class NoUseDependenceDeliverTest {
    public static void main(String[] args) {
        Person1 person1 = new Person1();
        person1.receviceEmail(new Email1());
        person1.receviceQQ(new QQ1());
        person1.receviceWeChat(new WeChat1());
    }
}

/**
 * 通过Person1实现接口消息的功能
 *      缺点:接受QQ要写一个方法,接收邮件要写一个方法
 */
class Person1{
    public void receviceEmail(Email1 email1){
        System.out.println(email1.getEmail());
    }
    public void receviceQQ(QQ1 qq1){
        System.out.println(qq1.getQQ());
    }
    public void receviceWeChat(WeChat1 weChat){
        System.out.println(weChat.getWeChat());
    }
}
class Email1 {
   public String getEmail(){
        return "电子邮件信息:HelloWorld";
   }
}
class QQ1 {
   public String getQQ(){
        return "QQ信息:收到了QQ消息";
   }
}
class WeChat1 {
    public String getWeChat(){
        return "微信信息:收到了微信消息";
    }
}


3.5.2.2.使用依赖倒转原则代码

3.5.2.2.1.依赖倒转原则简单案例
package com.dragonsoft.designpattern.basic.principles.dependenceinversion;

/**
 * 遵循依赖反转原则
 * 面向接口编程
 *      基于接口传递实现依赖
 */
public class UseDependenceDeliverTest {
    public static void main(String[] args) {
        //面向接口编程
        Person2 person2 = new Person2();
        person2.getMessage(new Email2());
        person2.getMessage(new QQ2());
        person2.getMessage(new WeChat2());
    }

}

class Person2 {
    public void getMessage(MessageInterface messageInterface) {
        System.out.println(messageInterface.sendMessage());
    }
}

interface MessageInterface {
    String sendMessage();
}

class Email2 implements MessageInterface{
    @Override
    public String sendMessage() {
        return  "电子邮件信息:HelloWorld";
    }
}

class QQ2 implements MessageInterface{
    @Override
    public String sendMessage() {
        return "QQ信息:收到了QQ消息";
    }
}
class WeChat2 implements MessageInterface{
    @Override
    public String sendMessage(){
        return "微信信息:收到了微信消息";
    }
}

3.5.2.2.2.通过接口传递依赖
package com.dragonsoft.designpattern.basic.principles.dependenceinversion;

/**
 * 依赖关系传递的三种方式
 *  第一种方式:通过接口传递依赖
 */
public class DependenceDeliver1 {
    public static void main(String[] args) {
        OpenAndClose1 openAndClose1 = new OpenAndClose1();
        openAndClose1.open(new ITV1() {
            @Override
            public void play() {
                System.out.println("ITV1播放......");
            }
        });
    }
}

interface ITV1{
    public void play();
}
interface IOpenAndClose1{
    public void open(ITV1 iTv1);
}
class OpenAndClose1 implements IOpenAndClose1{

    @Override
    public void open(ITV1 iTv1) {
        iTv1.play();
    }
}

3.5.2.2.3.通过构造方法传递依赖
package com.dragonsoft.designpattern.basic.principles.dependenceinversion;

/**
 * 依赖关系传递的三种方式
 *  第二种方式:通过构造方法传递依赖
 */
public class DependenceDeliver2 {
    public static void main(String[] args) {
        OpenAndClose2 openAndClose2 = new OpenAndClose2(new ITV2() {
            @Override
            public void play() {
                System.out.println("ITV2播放......");
            }
        });
        openAndClose2.open();
    }
}

interface ITV2{
    public void play();
}

interface IOpenAndClose2{
    public void open();
}

class OpenAndClose2 implements IOpenAndClose2{

    public ITV2 iTv2;
    public OpenAndClose2(ITV2 iTv2) {
        this.iTv2 = iTv2;
    }

    @Override
    public void open() {
        this.iTv2.play();
    }
}

3.5.2.2.4.通过setter()方法传递依赖
package com.dragonsoft.designpattern.basic.principles.dependenceinversion;

/**
 * 依赖关系传递的三种方式
 *  第三种方式:通过setter()方法传递依赖
 */
public class DependenceDeliver3 {
    public static void main(String[] args) {
        OpenAndClose3 openAndClose3 = new OpenAndClose3();
        openAndClose3.setItv3(new ITV3() {
            @Override
            public void play() {
                System.out.println("ITV3播放......");
            }
        });
        openAndClose3.open();
    }
}

interface ITV3{
    public void play();
}

interface IOpenAndClose3 {
    public void open();
}

class OpenAndClose3 implements IOpenAndClose3{

    private ITV3 itv3;

    public void setItv3(ITV3 itv3) {
        this.itv3 = itv3;
    }

    @Override
    public void open() {
        this.itv3.play();
    }
}

3.6.里氏替换原则LSP

3.6.1.里氏替换原则介绍

概述
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合、组合、依赖来解决问题

里氏替换原则是为了解决了什么问题
a.正确的使用继承
b.将原来的继承关系转换为继承基类+聚合/组合/依赖关系

里氏替换原则详细说明
a.所有能使用父类的地方,都能透明的使用该父类的子类对象
即继承时,子类尽量不要重写父类的方法,除非迫不得已,如果非要重写,就再写一个更基础的类,把这个要重写的方法提到基类里面去
b.遵循里氏替换原则,把继承这种高耦合的关系转换为聚合、组合、依赖这几种低耦合的关系

3.6.2.里氏替换原则代码

3.6.2.1.不使用里氏替换原则代码

package com.dragonsoft.designpattern.basic.principles.liskov_substitution;

/**
 * 未使用里氏替换原则
 * B1类继承了A1类,在不知道的情况下重下了父类A1的fun1()方法,外部调用子类B1类中中fun1()的时候以为
 *      里面的逻辑还是a-b,其实逻辑已经在不知道的情况下被重写成a+b了,导致计算结果出了问题
 *
 * 缺点:A1和B1的耦合性太高了,修改B很容易对A造成影响
 */
public class Liskov1 {
    public static void main(String[] args) {
        A1 a1 = new A1();
        System.out.println("11-3 = "+a1.fun1(11,3));
        System.out.println("1-8 = "+a1.fun1(1,8));

        A1 b1 = new B1();
        System.out.println("11-3 = "+b1.fun1(11,3));
        System.out.println("1-8 = "+b1.fun1(1,8));

    }
}

class A1 {
    /**
     * 返回 a与b的差
     * @param a
     * @param b
     * @return
     */
    public int fun1(int a,int b){
        return a-b;
    }

    public void printHelloWorld(){
        System.out.println("Hello World!");
    }
}

/**
 * B1类继承了A1类,在不知道的情况下重下了父类A的fun1()方法
 */
class B1 extends A1 {

    /**
     * 返回 a与b的和
     * @param a
     * @param b
     * @return
     */
    public int fun1(int a,int b){
        return a+b;
    }

    /**
     * B1独有方法,非继承自A1
     * 返回 a与b的和加上9
     * @param a
     * @param b
     * @return
     */
    public int fun2(int a,int b){
        return fun1(a,b) + 9;
    }

    public void printHelloWorld(){
        System.out.println("Hello World!");
    }
}

3.6.2.2.使用里氏替换原则代码

package com.dragonsoft.designpattern.basic.principles.liskov_substitution;

/**
 * 使用里氏替换原则:
 *  1.B2类不再继承A2类,改为A2类和B2类都继承一个基类Base
 *  2.这样调用者通过查看继承关系,明显的就知道了B2类中的fun1()里面的逻辑是a+b,而不是a-b
 *  3.把A2和B2中的公用方法printHelloWorld()抽取到了Base中
 *
 *  注意:本来B2继承了A2,可以直接调用父类A2中的方法,里氏替换原则修改代码后,B2不再继承A2,B2想要调用A2中的方法则可以通过依赖A2来实现调用
 */
public class Liskov2 {
    /**
     * 将A2和B2中的公用方法printHelloWorld()抽取到了Base中
     */
    public void printHelloWorld(){
        System.out.println("Hello World!");
    }
}


class Base{
    //把更加基础的方法和成员写到Base类中
}
class A2 extends Base{
    /**
     * 返回 a与b的差
     * @param a
     * @param b
     * @return
     */
    public int fun1(int a,int b){
        return a-b;
    }
}

class B2 extends Base {

    //B通过这个方式依赖A
    private A2 a2 = new A2();

    /**
     * 返回 a与b的和
     * @param a
     * @param b
     * @return
     */
    public int fun1(int a,int b){
        return a+b;
    }

    /**
     * B2独有方法,非继承自A2
     * 返回 a与b的和加上9
     * @param a
     * @param b
     * @return
     */
    public int fun2(int a,int b){
        return fun1(a,b) + 9;
    }

    /**
     * 里氏替换原则,如果B2和A2同时继承了基类Base,那么当B2想要使用A2类中的方法的时候,可以通过依赖的方式来调用A类2中的方法
     * @param a
     * @param b
     * @return
     */
    public  int fun3(int a,int b){
        return this.a2.fun1(a,b);
    }
}

3.7.开闭原则OCP

3.7.1.开闭原则介绍

概述
对修改关闭,对扩展开放

开闭原则详细说明
a.开闭原则是编程中最基础,最为重要的原则
b.一个软件实体,函数模块应该对扩展开放(对提供功能的一方而言),对修改关闭(对调用方/使用功能的一方而言,注意:不是对于客户端开放,客户端调用方法的使用方)。换而言之,当当增加一个新的类或者方法后,原先正在使用的代码不会收到丝毫影响,概括的说:对扩展开放,对修改关闭,即用抽象构建框架,用实现扩展细节
c.当软件需要变化时,尽量通过扩展软件的实体行为来实现变化,而不是通过修改已有的代码去实现,简单的说,就是通过扩展而不是通过修改已有代码
d.使用设计模式就是为了使代码更遵守开闭原则,是最重要的原则,其他的设计模式都是为了达到开闭原则的效果
e.遵循OPC原则设计出来的系统意味着具有相当的稳定性,至少在增加新功能的时候不会影响旧的功能,因为新的功能是通过扩展原来功能,而不是修改原来的功能,在代码层面的体现是新增一个功能不会动原来的代码,而是把新的功能写在新的类中

3.7.2.开闭原则代码

3.7.2.1.不使用开闭原则代码

package com.dragonsoft.designpattern.basic.principles.open_close;

/**
 * 不遵循OCP原则,目前只有一个画三角形的需求,当增加一个画圆形的需求的时候,
 * 不仅要对方法的提供者Painter做修改,也要对调用者做修改
 *
 */
public class OCPTest1 {
    public static void main(String[] args) {
        Painter1 painter1 = new Painter1();
        painter1.drawShape(new Triagle1());
        painter1.drawShape(new Cricle1());
    }
}
/**
 * 方法的使用方:
 *      不遵守OCP,扩展的时候要对方法的使用方做修改
 */
class Painter1 {
    public void drawShape(Shape1 shape){
        if(shape.type == 1) {
            drawTriangle();
        }else if(shape.type == 2) {
            drawCircle();
        }
    }
    private void drawTriangle(){
        System.out.println("画三角形...");
    }
    private void drawCircle(){
        System.out.println("画圆形...");
    }
}
class Shape1 {
    public int type;
}

/**
 * 方法的提供方
 */
class Triagle1 extends Shape1{
    public Triagle1(){
        super.type = 1;
    }
}
/**
 * 方法的提供方
 */
class Cricle1 extends Shape1{
    public Cricle1(){
        super.type = 2;
    }
}

3.7.2.2.使用开闭原则代码

package com.dragonsoft.designpattern.basic.principles.open_close;

public class OCPTest2 {
    public static void main(String[] args) {
        Painter2 painter2 = new Painter2();
        painter2.drawShape(new Triagle2());
        painter2.drawShape(new Cricle2());
    }
}

/**
 * 方法的使用方:
 *      遵守OCP,扩展的时候不用再对方法的使用方做修改
 */
class Painter2 {
    public void drawShape(Shape2 shape){
       shape.draw();
    }
}
abstract class Shape2 {
    public int type;
    public abstract void draw();
}
/**
 * 方法的提供方
 */
class Triagle2 extends Shape2{
    public Triagle2(){
        super.type = 1;
    }

    @Override
    public void draw() {
        System.out.println("画三角形......");
    }
}
/**
 * 方法的提供方
 */
class Cricle2 extends Shape2{
    public Cricle2(){
        super.type = 2;
    }

    @Override
    public void draw() {
        System.out.println("画圆形......");
    }
}

3.8.迪米特原则LoD

3.8.1.迪米特原则介绍

概述
只与直接的朋友通信,最少知道原则,避免与非直接朋友的耦合,对自己依赖的类知道的越少越好,对于被依赖的类不管多复杂,都尽量将逻辑封在内部。需要注意的是降低类之间的耦合,只是减少不必要的耦合,并不是要求完全解除耦合

迪米特原则详细说明
a.一个对象应该对其他对象保持最少的了解
b.类与类关系越密切,耦合度越大
c.迪米特原则又称最少知道原则,即一个类对自己依赖的类知道的越少越好,也就是说,对于被依赖的类不管多么复杂,都应该尽量将逻辑封装在类的内部,除了对外提供public方法之外,不对外泄露任何信息
e.迪米特方法还有个更简单的定义: 只与直接朋友通信
    直接朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就直接说这两个对象之间是朋友关系。耦合的方式有很多依赖、关联、组合、聚合,其中我们将出现在成员变量,方法参数,方法返回值这几个位置中类称为直接朋友,而出现在局部变量中的类不是直接朋友,也就是说,陌生的类最好不要以局部变量的方式出现在类的内部

直接朋友和间接朋友
直接朋友:出现在返回值、成员变量、方法参数中的类
间接朋友:出现在方法中作为局部变量的类
class A{
    public void fun(){}
}
class C{
    public void fun(){}
}
class B{
    //C就是B的直接朋友:出现在成员变量位置
    private C c = new C();
    //C就是B的直接朋友:出现在方法参数位置
    public void test1(C c){
        c.fun();
    }
    //C就是B的直接朋友:出现在返回值位置
    public C test3(){
        return null;
    }
    public void test4(){
        //A就是B的间接朋友:出现在方法中作为局部变量位置
        A a = new A();
        a.fun();
    }
}

3.8.2.迪米特原则代码

3.8.2.1.不使用迪米特原则代码

package com.dragonsoft.designpattern.basic.principles.demeter;

import java.util.ArrayList;
import java.util.List;

/**
 * 未遵守迪米特法则
 */
public class Demeter1 {
    public static void main(String[] args) {
        SchoolManager1 schoolManager1 = new SchoolManager1();
        schoolManager1.printAllEmployee(new CollegeManager1());
    }
}

/**
 * 学校总部员工类
 */
class Employee1 {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

/**
 * 学院员工类
 */
class CollegeEmployee1 {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

/**
 * 学院员工管理类
 */
class CollegeManager1 {
    public List<CollegeEmployee1> getAllCollegeEmployee(){
        List<CollegeEmployee1> list = new ArrayList<CollegeEmployee1>();
        //增加10个员工到学院
        for(int i=0; i<10; i++){
            CollegeEmployee1 collegeEmployee = new CollegeEmployee1();
            collegeEmployee.setId("学院员工id" + i);
            list.add(collegeEmployee);
        }
        return list;
    }
}

/**
 * 学校总部员工管理类
 *  SchoolManager的直接朋友:Employee、CollegeManager
 *  SchoolManager的非直接朋友:CollegeEmployee,这样就 违反了迪米特法则(只与直接的朋友通信,最少知道原则,避免与非直接朋友的耦合)
 */
class SchoolManager1 {
    public List<Employee1> getAllEmployee(){
        List<Employee1> list = new ArrayList<Employee1>();
        //增加5个员工到学校总部
        for(int i=0; i<5; i++){
            Employee1 employee1 = new Employee1();
            employee1.setId("学校总部员工id" + i);
            list.add(employee1);
        }
        return list;
    }

    /**
     * 输出学校总部和学院员工信息
     * @param collegeManager1
     */
    public void printAllEmployee(CollegeManager1 collegeManager1){
        List<CollegeEmployee1> allCollegeEmployee = collegeManager1.getAllCollegeEmployee();
        System.out.println("--------------学院员工--------------");
        for (CollegeEmployee1 collegeEmployee1 : allCollegeEmployee) {
            System.out.println(collegeEmployee1.getId());
        }

        List<Employee1> allEmployee = this.getAllEmployee();
        System.out.println("--------------学校总部员工--------------");
        for (Employee1 employee1 : allEmployee) {
            System.out.println(employee1.getId());
        }
    }
}

3.8.2.2.使用迪米特原则代码

package com.dragonsoft.designpattern.basic.principles.demeter;

import java.util.ArrayList;
import java.util.List;

/**
 * 使用迪米特法则改进
 */
public class Demeter2 {
    public static void main(String[] args) {
        SchoolManager2 schoolManager2 = new SchoolManager2();
        schoolManager2.printAllEmployee(new CollegeManager2());
    }
}

/**
 * 学校总部员工类
 */
class Employee2 {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

/**
 * 学院员工类
 */
class CollegeEmployee2 {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

/**
 * 学院员工管理类
 */
class CollegeManager2 {
    public List<CollegeEmployee2> getAllCollegeEmployee(){
        List<CollegeEmployee2> list = new ArrayList<CollegeEmployee2>();
        //增加10个员工到学院
        for(int i=0; i<10; i++){
            CollegeEmployee2 collegeEmployee2 = new CollegeEmployee2();
            collegeEmployee2.setId("学院员工id" + i);
            list.add(collegeEmployee2);
        }
        return list;
    }
    /**
     * 输出学院员工信息
     * @param
     */
    public void printAllEmployee() {
        List<CollegeEmployee2> allCollegeEmployee = this.getAllCollegeEmployee();
        System.out.println("--------------学院员工--------------");
        for (CollegeEmployee2 collegeEmployee2 : allCollegeEmployee) {
            System.out.println(collegeEmployee2.getId());
        }
    }
}

/**
 * 学校总部员工管理类
 */
class SchoolManager2 {
    public List<Employee2> getAllEmployee(){
        List<Employee2> list = new ArrayList<Employee2>();
        //增加5个员工到学校总部
        for(int i=0; i<5; i++){
            Employee2 employee2 = new Employee2();
            employee2.setId("学校总部员工id" + i);
            list.add(employee2);
        }
        return list;
    }

    /**
     * 输出学校总部员工信息
     * @param collegeManager2
     */
    public void printAllEmployee(CollegeManager2 collegeManager2){
        //输出学院员工信息
        collegeManager2.printAllEmployee();

        //输出学校员工信息
        List<Employee2> allEmployee = this.getAllEmployee();
        System.out.println("--------------学校总部员工--------------");
        for (Employee2 employee2 : allEmployee) {
            System.out.println(employee2.getId());
        }

    }
}

3.9.合成复用原则CARP

3.9.1.合成复用原则介绍

概述
尽量使用合成/聚合方法,避免使用继承

使用合成复用原则有什么效果
当A继承B后,A拥有B所有的方法,使用合成复用改进后,A可以调用B中的方法,不用的方法就不出现在B中了

合成复用原则与继承原则关系
在编程中,推荐尽量使用合成复用原则来代替继承,但是反过来,可以使用合成复用原则解决的问题也可以使用继承解决

3.9.2.合成复用原则代码

3.9.2.1.不使用合成复用原则代码

package com.dragonsoft.designpattern.basic.principles.compositereuse;

/**
 * 合成复用原则
 */
public class CompositeReuse1 {
}

/**
 * 继承关系
 * 如果想让B1使用A1的方法,可以让B1继承A1,不过,这样会加重B1和A1的耦合
 */
class A1{
    public void fun1(){}
    public void fun2(){}
    public void fun3(){}
}

class B1 extends A1 {

}

3.9.2.2.使用合成复用原则代码

package com.dragonsoft.designpattern.basic.principles.compositereuse;

/**
 * 合成复用原则
 */
public class CompositeReuse2 {
}

/**
 * 代替继承关系的方式一:依赖关系
 * 如果想让B1使用A1的方法,B2依赖A2,即在B2的方法参数中传入A2,然后再通过调用A2对象的方法来实现B2使用A2中的方法
 */
class A2{
    public void fun1(){}
    public void fun2(){}
    public void fun3(){}
}

class B2{
    public void fun1(A2 a2){
        a2.fun1();
    }
    public void fun2(A2 a2){
        a2.fun2();
    }
    public void fun3(A2 a2){
        a2.fun3();
    }
}

/**
 * 代替继承关系的方式二:聚合关系
 * 把A3聚合到B3中,即A3作为B3的属性出现在B3中,并且通过setter()方法为B3中的A3注入值
 */
class A3{
    public void fun1(){}
    public void fun2(){}
    public void fun3(){}
}

class B3{
    private A3 a3;

    public void setA3(A3 a3) {
        this.a3 = a3;
    }

    public void fun1(){
        a3.fun1();
    }
    public void fun2(){
        a3.fun2();
    }
    public void fun3(){
        a3.fun3();
    }
}

/**
 * 代替继承关系的方式三:组合关系
 * 让A4作为B4的属性出现在B4中,而且当B4创建完成后A4就创建完成了,称为把A4组合到B4中
 * 让A4作为B5的属性出现在B5中,而且当B5创建完成后A4就创建完成了,称为把A5组合到B4中
 */
class A4{
    public void fun1(){}
    public void fun2(){}
    public void fun3(){}
}
//通过直接new给成员变量赋值
class B4{
    private A4 a4 = new A4();

    public void fun1(){
        a4.fun1();
    }
    public void fun2(){
        a4.fun2();
    }
    public void fun3(){
        a4.fun3();
    }
}
//通过构造方法给成员变量赋值
class B5{
    private A4 a4;
    public B5(A4 a4){
        this.a4 = a4;
    }

    public void fun1(){
        a4.fun1();
    }
    public void fun2(){
        a4.fun2();
    }
    public void fun3(){
        a4.fun3();
    }
}

上次编辑于: 2022/9/10 01:42:04
贡献者: lingwh
评论