java 7大设计原则

一、设计模式七大原则

设计模式的目的

  • 代码重用性 (即:相同功能的代码,不用多次编写)
  • 可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
  • 可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
  • 可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
  • 高内聚低耦合:使程序呈现高内聚,低耦合的特性

有点官方,反正就是牛逼!

七大原则:

1.单一职责原则

每个类只负责一个职责

2.接口隔离原则

将接口细分为多个接口,保证实现接口的类不重写自己不需要的方法

3.依赖倒转原则

用 抽象类 或者 接口,然后由具体的类去继承、实现它,重写其方法,实现依赖倒转 类似 多态
注意:变量的声明类型尽量是抽象类或接口!!!

4.里氏替换原则

子类中尽量不要重写父类的方法

5.开闭原则

一个软件实体 如类、模块、函数 应该对 扩展新功能 开放,对修改旧功能关闭。
接口或者抽象类 内置 方法,让其实现类 重写方法满足其需要

6.迪米特原则

简单来说: 一个类中 只与 [ 直接的朋友 ] 产生依赖 如果是 陌生类,就违背 迪米特法则

什么是直接朋友:

1、成员变量: A类中的 全局变量 有 private B b; B是A的直接朋友
2、方法参数:A类中 M1方法的 请求参数 为 B b -> m1(B b){} A与B是直接朋友
3、方法返回值的类: A类中的方法 M2 的返回值为B -> public B M2(){} A与B是直接朋友

7.合成复用原则

类和类之间 尽量使用 合成 和聚合的方式 而不是使用 继承

1.1、单一职责原则

介绍:

一个类应该只负责一项职责。如类 A 负责两个不同职责:职责 1,职责 2。当职责 1 需求变更 而改变 A 时,可能造成职责 2 执行错误

以 交通工具为 案例
不使用 单一职责原则 Demo:

public class A_SingleResponsibilityPrinciple {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("汽车");
        vehicle.run("飞机");
        vehicle.run("鲨鱼");
    }

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

理解:Vehicle 类的 run 方法 职责是 在 公路上跑 的职责,并不负责在 天上飞、水里游的职责。 

使用 单一职责原则 Demo:

public class C_SingleResponsibilityPrinciple {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("汽车");
        vehicle.runAir("飞机");
        vehicle.runWater("鲨鱼");
    }

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

        public void runAir(String vehicle){
            System.out.println(vehicle + "在天上跑");
        }

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


}

理解:Vehicle 类的
run方法 只负责在 公路上跑 ;
runAir 负责在天上跑
runWater负责在水里跑
每个方法都只对应 一个职责,是为 单一职责原则。 

单一职责原则注意事项和细节

  1. 降低类的复杂度,一个类只负责一项职责。
  2. 提高类的可读性,可维护性
  3. 降低变更引起的风险
  4. 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以
    在代码级违反单一职责原则;只有类中 方法数量足够少,可以在方法级别保持单一职责原则

1.2、接口隔离原则

接口隔离原则:一个类不需要实现他不需要的接口。即:一个类对另一个类的依赖应该建立在最小的接口上。

理解:

我们一个类A 通过 接口去依赖另一个类B 我们希望这个接口是最小的(不包含A不需要的方法)
就是 原本接口 I1有很多方法,A只想用其中 几个 就把其中几个单独拿出来做个接口I2 ,B去继承这来接口

不使用接口隔离原则的Demo

/**
 * 接口隔离原则:一个类不需要实现他不需要的接口。即:一个类对另一个类的依赖应该建立在最小的接口上
 * 翻译一下就是:我们一个类A 通过 接口去依赖另一个类B 我门希望这个接口是最小的(不包含A不需要的方法)
 * 就是 原本接口I1 有很多方法,A只想用其中 几个 就把其中几个单独拿出来做个接口I22 ,B去继承这来接口
 *
 * 不使用 接口隔离原则
 */
public class A_InterfaceLsolationPrinciple {
    public static void main(String[] args) {
        // A 通过 接口 去依赖B类  A只用 1 2 3  但 B 实现了接口的所有方法【12345】 不符合 接口的 隔离原则
        A a = new A();
        a.do1(new B());
        a.do2(new B());
        a.do3(new B());

        C c = new C();
        c.do1(new D());
        c.do4(new D());
        c.do5(new D());

    }


    // 接口
    interface Interface1 {

        void run1();

        void run2();

        void run3();

        void run4();

        void run5();

    }

    // 接口
    interface Interface3 extends Interface1{

        void run6();

    }

    static class B implements Interface1 {

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

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

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

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

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

    static class D implements Interface1{

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

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

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

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

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

    /**
     * A 通过接口 Interface1 依赖(使用)B类 ,但 只用 1 2 3 方法
     */
    static class A{
        public void do1(Interface1 i){
            i.run1();
        }
        public void do2(Interface1 i){
            i.run2();
        }
        public void do3(Interface1 i){
            i.run3();
        }
    }

    /**
     * C 通过接口 Interface1 依赖(使用)D类 ,但 只用 1 4 5 方法
     */
    static class C{
        public void do1(Interface1 i){
            i.run1();
        }
        public void do4(Interface1 i){
            i.run4();
        }
        public void do5(Interface1 i){
            i.run5();
        }
    }


}

代码解释:
一个接口 Interface1 有5个方法

    // 接口
    interface Interface1 {
        void run1();
        void run2();
        void run3();
        void run4();
        void run5();
    }

B、D两个类实现 接口 Interface1 .并重写了 接口的五个方法

static class B implements Interface1 {

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

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

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

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

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

    static class D implements Interface1{

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

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

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

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

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

重点来了:
A类 通过接口 Interface1 依赖(使用)B类,但 只用 1 2 3 方法

A 通过 接口 去依赖B类 A只用 1 2 3 但 B 实现了接口的所有方法【12345】 不符合 接口的 隔离原则

C 通过接口 Interface1 依赖(使用)D类 ,但 只用 1 4 5 方法

C 通过 接口 去依赖D类 C只用 1 4 5 但 D 实现了接口的所有方法【12345】 不符合 接口的 隔离原则

static class A{
        public void do1(Interface1 i){
            i.run1();
        }
        public void do2(Interface1 i){
            i.run2();
        }
        public void do3(Interface1 i){
            i.run3();
        }
    }

    /**
     * C 通过接口 Interface1 依赖(使用)D类 ,但 只用 1 4 5 方法
     */
    static class C{
        public void do1(Interface1 i){
            i.run1();
        }
        public void do4(Interface1 i){
            i.run4();
        }
        public void do5(Interface1 i){
            i.run5();
        }
    }

main 方法

  public static void main(String[] args) {
        // A 通过 接口 去依赖B类  A只用 1 2 3  但 B 实现了接口的所有方法【12345】 不符合 接口的 隔离原则
        A a = new A();
        a.do1(new B());
        a.do2(new B());
        a.do3(new B());

        C c = new C();
        c.do1(new D());
        c.do4(new D());
        c.do5(new D());
    }

改进 使用接口隔离原则

public class B_InterfaceLsolationPrinciple {
    public static void main(String[] args) {
        A a = new A();
        a.do1(new B());
        a.do2(new B());
        a.do3(new B());

        C c = new C();
        c.do1(new D());
        c.do4(new D());
        c.do5(new D());
    }

    // 接口
    interface Interface1 {
        void run1();
    }

    // 接口
    interface Interface2 {
        void run2();
        void run3();
    }

    // 接口
    interface Interface3 {
        void run4();
        void run5();
    }

    static class B implements Interface1 ,Interface2{
        Interface1 i1 =new B();
        @Override
        public void run1() {
            System.out.println("B实现 run1");
        }

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

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

    }

    static class D implements Interface1,Interface3{

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

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

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

    /**
     * A 通过接口 Interface1 依赖(使用)B类 ,但 只用 1 2 3 方法
     */
    static class A{
        public void do1(Interface1 i){
            i.run1();
        }
        public void do2(Interface2 i){
            i.run2();
        }
        public void do3(Interface2 i){
            i.run3();
        }
    }

    /**
     * C 通过接口 Interface1 依赖(使用)D类 ,但 只用 1 4 5 方法
     */
    static class C{
        public void do1(Interface1 i){
            i.run1();
        }
        public void do4(Interface3 i){
            i.run4();
        }
        public void do5(Interface3 i){
            i.run5();
        }
    }
}

核心代码解释
将 原本的 接口一拆分成三个接口,分别为123
因为A 类只用 1 2 3 方法 ;D 只用 1 4 5 方法

  // 接口
    interface Interface1 {
        void run1();
    }

    // 接口
    interface Interface2 {
        void run2();
        void run3();
    }

    // 接口
    interface Interface3 {
        void run4();
        void run5();

    }

B 类实现 接口 1、2 并重写其方法 A 类只用 1 2 3 方法
D 类实现 接口 1、3并重写其方法 D 只用 1 4 5 方法

static class B implements Interface1 ,Interface2{
        Interface1 i1 =new B();
        @Override
        public void run1() {
            System.out.println("B实现 run1");
        }

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

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

static class D implements Interface1,Interface3{

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

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

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

通过拆分接口、实现接口 隔离原则

1.3、依赖倒置原则

说点 字都认识 但放在一起就不认识的吧

  1. 高层模块不应该依赖低层模块,二者都应该依赖其抽象
  2. 抽象不应该依赖细节,细节应该依赖抽象
  3. 依赖倒转(倒置)的中心思想是面向接口编程
  4. 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架 构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
  5. 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完 成

应该 、大概、也许就是 用 抽象类 或者 接口,然后由具体的类去继承、实现它,重写其方法,实现依赖倒转 类似 多态
注意:变量的声明类型尽量是抽象类或接口!!!

Person 接收消息的功能

不用 依赖倒转原则

public class A_RelyOnTheInversionPrinciple {
    public static void main(String[] args) {
        new Person().receive(new Email());
    }

    static class Email{
        public String getInfo(){
            return "电子邮件信息:hello";
        }
    }


    /**
     * 完成Person接收消息的功能
     */
    static class Person{
        public void receive(Email email){
            System.out.println(email.getInfo());
        }
    }
}

理解:

1、简单比较容易想到
2、如果我们获取的对象是 微信、短信等,而不是 电子邮件,那么就要增加 新类 person 也要增加对应的接收方法

改进:

引入一个 抽象接口:IReceive,表示接收者, 这样Person类 与IReceive发生依赖
Email \微信 等属于接收的范畴,可以实现 IReceive接口
这就是 依赖倒转原则 类似 多态

public class B_RelyOnTheInversionPrinciple {

    public static void main(String[] args) {
        new Person().receive(new Email());
        new Person().receive(new QQ());
    }

    // 收到消息的 接口
    interface IReceive{
       String getInfo();
    }

    // 邮件收到消息
    static class Email implements IReceive{

        @Override
        public String getInfo() {
            return "电子邮件信息:hello";
        }
    }
    // QQ收到消息
    static class QQ implements IReceive{

        @Override
        public String getInfo() {
            return "QQ信息:hello";
        }
    }

    /**
     * 完成Person接收消息的功能

     *  依赖倒转原则 实现: 引入一个 抽象接口:IReceive,表示接收者, 这样Person类 与IReceive发生依赖
     *     Email 、微信 等属于接收的范畴,可以实现 IReceive接口
     *
     * 类似 多态
     *
     */
    static class Person{
        public void receive(IReceive receive){
            System.out.println(receive.getInfo());
        }
    }
}

代码解释:
上面说到:抽象不应该依赖细节,细节应该依赖抽象
所以 先定义个抽象(接口),然后让细节(接口的实现类)去展示具体细节

1、首先定义 一个获取消息的接口

    // 收到消息的 接口
    interface IReceive{
       String getInfo();
    }

2、然后让 qq 、微信…去实现接口

    // 邮件收到消息
    static class Email implements IReceive{
        @Override
        public String getInfo() {
            return "电子邮件信息:hello";
        }
    }

    // QQ收到消息
    static class QQ implements IReceive{
        @Override
        public String getInfo() {
            return "QQ信息:hello";
        }
    }

实现了 细节依赖抽象啦。
人获取消息的方法:

   static class Person{
        public void receive(IReceive receive){
            System.out.println(receive.getInfo());
        }
    }

main:

    public static void main(String[] args) {
        new Person().receive(new Email());
        new Person().receive(new QQ());
    }

1.3.1、依赖传递的方式

多说几句。。。。。。
依赖关系的传递三种方式

1、接口传递
2、构造方法传递
3、setter方法传递

1、接口传递

    interface IOpenAndClose {
        void open(ITV tv);
    }

    interface ITV {
        void play();
    }

    static class hand implements ITV {
        @Override
        public void play() {
            System.out.println("用手关");
        }
    }

    // 实现接口
    static class OpenAndClose implements IOpenAndClose {

        @Override
        public void open(ITV tv) {
            tv.play();
        }
    }

解释:

OpenAndClose 方法 实现了 IOpenAndClose 接口 ,但是呢,IOpenAndClose 的方法使用了
ITV接口 所以 OpenAndClose 就 通过 IOpenAndClose (依赖)使用了 ITV 是为 : 接口传递

2、构造方法传递

public class D_DependOnTheTransfer {
    public static void main(String[] args) {
        new OpenAndClose(new hand()).open();
    }

    /**
     * 方式 2、构造方法传递
     * <p>
     * 开关接口
     */
    interface IOpenAndClose {
        void open();
    }

    interface ITV {
        void play();
    }

    static class hand implements ITV {
        @Override
        public void play() {
            System.out.println("用手关");
        }
    }

    // 实现接口
    static class OpenAndClose implements IOpenAndClose {

        public ITV tv;
        public OpenAndClose(ITV tv){ // 构造器
            this.tv = tv;
        }

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

解释:

    static class OpenAndClose implements IOpenAndClose {

        public ITV tv;
        public OpenAndClose(ITV tv){ // 构造器
            this.tv = tv;
        }

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

OpenAndClose 类的构造方法 参数为 ITV接口,so 通过 构造方法和 ITV产生了依赖

3、setter 方法传递

public class E_DependOnTheTransfer {
    public static void main(String[] args) {
        OpenAndClose openAndClose = new OpenAndClose();
        openAndClose.setTv(new hand());
        openAndClose.open();
    }

    /**
     * 方式 3、setter方法传递
     * <p>
     * 开关接口
     */
    interface IOpenAndClose {
        void open();
        void setTv(ITV tv);
    }

    interface ITV {
        void play();
    }

    static class hand implements ITV {
        @Override
        public void play() {
            System.out.println("用手关");
        }
    }

    // 实现接口
    static class OpenAndClose implements IOpenAndClose {

        public ITV tv;

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

        @Override
        public void setTv(ITV tv) {
            this.tv = tv;
        }
    }
}

 解释:

    static class OpenAndClose implements IOpenAndClose {

        public ITV tv;

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

        @Override
        public void setTv(ITV tv) {
            this.tv = tv;
        }
    }

 

此处 通过 set方法的参数 ITV ,OpenAndClose 和 ITV 产生了依赖

依赖倒转原则的注意事项和细节:

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

1.4、里氏替换原则

里氏替换原则的必要性:
继承包含这样一层含义:
1、父类中凡是已经实现好的方法,虽然他不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
2、如果一个类被其他类所继承,则当这个类需要任意修改,就必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都可能产生故障

废话不说,就一句话:

子类中尽量不要重写父类的方法

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

案例:
不使用里氏替换原则

    static class A {
        public int func1(int num1, int num2) {
            return num1 - num2;
        }
    }

    static class B extends A {
        // 重写了A 的方法
        public int func1(int a, int b) {
            return a + b;
        }

        public int func2(int a,int b){
            return func1(a,b) + 9;
        }
    }

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

        System.out.println("====================");

        B b = new B();
        System.out.println("11-3=" + b.func1(11,3));
        System.out.println("1-8=" + b.func1(1,8));

        System.out.println("11+3+9=" + b.func2(11,3));

    }

解释:B无意中重写的父类的func1方法, 原本func1 是 num1 - num2,重写后 变成 相加, 这样 如果是无意识的话,程序员本来要做 func1的相减功能,但是结果却是相加。造成BUG

改进:

public class B_RichterSubstitutionPrinciple {
    /**
     * 创建一个更加 基础的基类
     *
     */
    static class Base{
        //更加基础的方法和成员写到Base

    }

    static class A extends Base{
        public int func1(int num1, int num2) {
            return num1 - num2;
        }
    }

    static class B extends Base {
        // 如果B需要使用A类的方法,使用组合的关系
        private A a = new A();

        // 仍然想用A 的方法
        public int func3(int a,int b){
            return this.a.func1(a,b);
        }


        // 重写了A 的方法
        public int func1(int a, int b) {
            return a + b;
        }

        public int func2(int a,int b){
            return func1(a,b) + 9;
        }
    }


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

        System.out.println("====================");

        // 因为B类不再继承A 类,因此调用者不会在func1是求减法
        B b = new B();
        System.out.println("11-3=" + b.func3(11,3));
        System.out.println("1-8=" + b.func3(1,8));
        System.out.println("1+8=" + b.func1(1,8));
        System.out.println("11+3+9=" + b.func2(11,3));
    }

}

在这里插入图片描述 

代码解释:

创建一个更为基础的 类

   static class Base{
        //更加基础的方法和成员写到Base
   }

A、B 类 继承的是 base B类不再继承 A 类 就不存在重写的情况
B类中使用A类的 func1方法是 通过 new A 的方法(组合依赖解决问题)

    static class B extends Base {

        // 如果B需要使用A类的方法,使用组合的关系
        private A a = new A();

        // 仍然想用A 的方法
        public int func3(int a,int b){
            return this.a.func1(a,b);
        }

        // 重写了A 的方法
        public int func1(int a, int b) {
            return a + b;
        }

        public int func2(int a,int b){
            return func1(a,b) + 9;
        }
    }

1.5、开闭原则

最重要 最基础的原则 对扩展开放【提供者】 对修改关闭【使用方】
一个软件实体 如类、模块、函数 应该对 扩展新功能 开放,对修改旧功能关闭。
用抽象 构建 框架、用实体 扩展细节

案例 绘图
不使用开闭原则:

public class A_OpenClosePrinciple {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        // 矩形
        graphicEditor.drawShape(new GraphicEditor.Rectangle());
        // 圆形
        graphicEditor.drawShape(new GraphicEditor.Circle());

        // 三角形
        // 1
        graphicEditor.drawShape(new GraphicEditor.Triangle());
    }

    /**
     * 这是一个用于绘图的类
     */
    static class GraphicEditor{

        // 接收Shape对象,然后 根据type,来绘制不同的图形  【使用方】
        public void drawShape(Shape s){
            if (s.m_type == 1){
                drawRectangle(s);
            }else if (s.m_type == 2){
                drawCircle(s);
            }else if (s.m_type == 3){
                // 2
                drawTriangle(s);
            }
        }

        // 绘制矩形
        public void drawRectangle(Shape s){
            System.out.println("绘制矩形");

        }

        // 绘制圆
        public void drawCircle(Shape s){
            System.out.println("绘制圆");

        }

        // 绘制三角形
        // 3
        public void drawTriangle(Shape s){
            System.out.println("绘制三角形");

        }

        // Shape 类
        static class Shape{
            int m_type;
        }

        // 矩形
        static class Rectangle extends Shape{
            Rectangle(){
                super.m_type = 1;
            }
        }

        // 圆
        static class Circle extends Shape{
            Circle(){
                super.m_type = 2;
            }
        }

        // 新增三角形
        // 4
        static class Triangle extends Shape{
            Triangle(){
                super.m_type = 3;
            }
        }
    }
}

解释:

  • 优点: 好理解,简单易操作
  • 缺点:违反了设计模式的开闭原则,对扩展开放【提供者】 对修改关闭【使用方】
    即:当我们给类增加功能时,尽量不要修改代码或少修改代码
    如下:新增三角形 修改 4处

四处:

1、

// 三角形
// 1
graphicEditor.drawShape(new GraphicEditor.Triangle());

2、

else if (s.m_type == 3){
  // 2
  drawTriangle(s);
}

3、

 // 绘制三角形
 // 3
 public void drawTriangle(Shape s){
   System.out.println("绘制三角形");
 }

4、

 // 新增三角形
 // 4
 static class Triangle extends Shape{
   Triangle(){
     super.m_type = 3;
   }
 }

改进:
使用开闭原则:

将基类 Shape类 做成 抽象类,并提供一个抽象的draw方法,让子类去实现其需要

public class B_OpenClosePrinciple {

    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        // 矩形
        graphicEditor.drawShape(new GraphicEditor.Rectangle());
        // 圆形
        graphicEditor.drawShape(new GraphicEditor.Circle());

        // 三角形
        graphicEditor.drawShape(new GraphicEditor.Triangle());
        graphicEditor.drawShape(new GraphicEditor.pentagon());
    }

    /**
     * 这是一个用于绘图的类
     */
    static class GraphicEditor{

        // 接收Shape对象,然后 根据type,来绘制不同的图形  【使用方】
        public void drawShape(Shape s){
            s.draw();
        }

        // Shape 类
        static abstract class Shape{
            public abstract void draw();
        }

        // 矩形
        static class Rectangle extends Shape {
            @Override
            public void draw() {
                System.out.println("绘制矩形");
            }
        }

        // 圆
        static class Circle extends Shape {
            @Override
            public void draw() {
                System.out.println("绘制圆");
            }
        }

        // 新增三角形
        static class Triangle extends Shape {
            @Override
            public void draw() {
                System.out.println("绘制三角形");

            }
        }

        // 新增五角形
        static class pentagon extends Shape {
            @Override
            public void draw() {
                System.out.println("绘制五角形");
            }
        }
    }
}

解释:
创建一个 抽象绘图类

// Shape 类
static abstract class Shape{
    public abstract void draw();
}

各个图形继承这个歌类 重写其绘图方法

        // 矩形
        static class Rectangle extends Shape {

            @Override
            public void draw() {
                System.out.println("绘制矩形");
            }
        }

这样 每次增加一个图形 只需要 加一个类 继承 Shape接口即可

1.6、迪米特原则

看不懂的官方:

一个类 对自己依赖的类知道的越少越好。也就是说:对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供public方法,不对外泄露任何信息

简单来说: 一个类中 只与 [ 直接的朋友 ] 产生依赖 如果是 陌生类,就违背 迪米特法则

什么是直接朋友:

1、成员变量: A类中的 全局变量 有 private B b; B是A的直接朋友
2、方法参数:A类中 M1方法的 请求参数 为 B b -> m1(B b){} A与B是直接朋友
3、方法返回值的类: A类中的方法 M2 的返回值为B -> public B M2(){} A与B是直接朋友
之外就是 陌生的类。

目的:

1、迪米特原则:降低类之间的耦合
2、只要求降低 耦合度,而不是完全没有

案例:
不使用 迪米特原则

public class A_DemeterPrinciple {

    public static void main(String[] args) {
        //创建了一个 SchoolManager 对象
        SchoolManager schoolManager = new SchoolManager();
        //输出学院的员工 id 和 学校总部的员工信息
        schoolManager.printAllEmployee(new CollegeManager());

    }

    //学校总部员工类
    static class Employee {
        private String id;

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

        public String getId() {
            return id;
        }
    }


    //学院的员工类
    static class CollegeEmployee {
        private String id;

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

        public String getId() {
            return id;
        }
    }

    // 管理学院员工的管理类
    static class CollegeManager {
        // 返回学院的所有员工
        public List<CollegeEmployee> getAllEmployee() {
            List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
            for (int i = 0; i < 10; i++) { // 这里我们增加了 10 个员工到 list
                CollegeEmployee emp = new CollegeEmployee();
                emp.setId("学院员工 id= " + i);
                list.add(emp);
            }
            return list;
        }
    }

// 学校管理类
// 分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
// CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
    static class SchoolManager {
        // 返回学校总部的员工
        public List<Employee> getAllEmployee() {
            List<Employee> list = new ArrayList<Employee>();
            for (int i = 0; i < 5; i++) { //这里我们增加了 5 个员工到 list
                Employee emp = new Employee();
                emp.setId("学校总部员工 id= " + i);
                list.add(emp);
            }
            return list;
        }

        //该方法完成输出学校总部和学院员工信息(id)
        void printAllEmployee(CollegeManager sub) {

            /**
             *      分析问题
             *             1. 这里的 CollegeEmployee 不是 SchoolManager 的直接朋友
             *             2. CollegeEmployee 是以局部变量方式出现在 SchoolManager
             *             3. 违反了 迪米特法则
             */

            //获取到学院员工
            List<CollegeEmployee> list1 = sub.getAllEmployee();
            System.out.println("----------------学院员工--------------");
            for (CollegeEmployee e : list1) {
                System.out.println(e.getId());
            }


            //获取到学校总部员工
            List<Employee> list2 = this.getAllEmployee();
            System.out.println("------------学校总部员工-----------");
            for (Employee e : list2) {
                System.out.println(e.getId());
            }
        }
    }
}

其中:

void printAllEmployee(CollegeManager sub) {
            //获取到学院员工
            List<CollegeEmployee> list1 = sub.getAllEmployee();
            System.out.println("----------------学院员工--------------");
            for (CollegeEmployee e : list1) {
                System.out.println(e.getId());
            }
}

这里的 CollegeEmployee 不是 SchoolManager 的直接朋友
CollegeEmployee 是以局部变量方式出现在 SchoolManager
违反了 迪米特法则

改进:

public class B_DemeterPrinciple {

    public static void main(String[] args) {
        //创建了一个 SchoolManager 对象
        SchoolManager schoolManager = new SchoolManager();
        //输出学院的员工 id 和 学校总部的员工信息
        schoolManager.printAllEmployee(new CollegeManager());
    }

    //学校总部员工类
    static class Employee {
        private String id;

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

        public String getId() {
            return id;
        }
    }


    //学院的员工类
    static class CollegeEmployee {
        private String id;

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

        public String getId() {
            return id;
        }
    }


    // 管理学院员工的管理类
    static class CollegeManager {
        // 返回学院的所有员工
        public List<CollegeEmployee> getAllEmployee() {
            List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
            for (int i = 0; i < 10; i++) { // 这里我们增加了 10 个员工到 list
                CollegeEmployee emp = new CollegeEmployee();
                emp.setId("学院员工 id= " + i);
                list.add(emp);
            }
            return list;
        }

        // 输出学院员工的信息
        public void printEmployee(){
            //获取到学院员工
            List<CollegeEmployee> list1 = getAllEmployee();
            System.out.println("----------------学院员工--------------");
            for (CollegeEmployee e : list1) {
                System.out.println(e.getId());
            }
        }
    }

// 学校管理类
// 分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
// CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
    static class SchoolManager {
        // 返回学校总部的员工
        public List<Employee> getAllEmployee() {
            List<Employee> list = new ArrayList<Employee>();

            for (int i = 0; i < 5; i++) { //这里我们增加了 5 个员工到 list
                Employee emp = new Employee();
                emp.setId("学校总部员工 id= " + i);
                list.add(emp);
            }
            return list;
        }

        //该方法完成输出学校总部和学院员工信息(id)
        void printAllEmployee(CollegeManager sub) {


            /**
             * 1. 这里的 CollegeEmployee 不是 SchoolManager 的直接朋友
             * 2. CollegeEmployee 是以局部变量方式出现在 SchoolManager
             * 3. 违反了 迪米特法则
             *
             * 改进:
             *  1、将输出学院的员工方法,封装到 CollegeManager  对应到  被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部
             *
             */
            //获取到学院员工
            sub.printEmployee();

            //获取到学校总部员工
            List<Employee> list2 = this.getAllEmployee();
            System.out.println("------------学校总部员工-----------");
            for (Employee e : list2) {
                System.out.println(e.getId());
            }
        }
    }
}

重点:

       void printAllEmployee(CollegeManager sub) {

            //获取到学院员工
            sub.printEmployee();

            //获取到学校总部员工
            List<Employee> list2 = this.getAllEmployee();
            System.out.println("------------学校总部员工-----------");
            for (Employee e : list2) {
                System.out.println(e.getId());
            }
        }

将输出学院的员工方法,封装到 CollegeManager 对应到 被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部

       // 输出学院员工的信息
        public void printEmployee(){
            //获取到学院员工
            List<CollegeEmployee> list1 = getAllEmployee();
            System.out.println("----------------学院员工--------------");
            for (CollegeEmployee e : list1) {
                System.out.println(e.getId());
            }
        }

 

 

 

 

 

 

 

 

 


 

 

 

 




 

 

 

 

 
 


 

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值