java 7大设计原则

news2024/11/16 19:52:19

一、设计模式七大原则

设计模式的目的

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

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

七大原则:

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());
            }
        }

 

 

 

 

 

 

 

 

 


 

 

 

 




 

 

 

 

 
 


 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/452279.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于今年五一调休。。

作者主页&#xff1a;爱笑的男孩。的博客_CSDN博客-深度学习,YOLO,活动领域博主爱笑的男孩。擅长深度学习,YOLO,活动,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typecollect个人…

Spring5学习总结(三)JdbcTemplate实现数据库增删改查操作JdbcTemplate实现批量增删改操作

Spring5学习总结&#xff08;三&#xff09;JdbcTemplate实现数据库增删改查操作/JdbcTemplate实现批量增删改操作 一、JdbcTemplate概述 什么是 JdbcTemplate&#xff1f; JdbcTemplate是Spring 框架对 JDBC 进行的封装&#xff0c;使用它可以更方便实现对数据库的操作。 二…

Redis序列化设置以及jetcache连接Redis序列化的设置

1、问题 问题&#xff1a;我在使用jetchche进行连接redis的时候&#xff0c;存入redis的value一直使用的是redis默认的序列化方式&#xff0c;是使用的jdk序列化。当我使用jetcache向redis存入一个对象 存入redis的结果: 这是使用jdk序列化的结果。 但是我记得使用redis的时候…

密码学期末复习(按考点整理,超详细!!!)

复习目录&#xff09; 题型第一章密码算法的安全性分类密码分析的难易程度凯撒密码加密原理古典密码中仿射变换的解密变换 第二章流密码的组成和特点流密码的基本思想 第三章分组密码的CBC工作模式&#xff0c;以及该模式的加密示意图AES中字节求逆雪崩效应分组密码的安全设计原…

让HR眼前一亮:30个APP项目软件测试经验,点燃你的简历

在求职过程中&#xff0c;我们都希望自己的简历能够吸引面试官的眼球&#xff0c;从而获得更多的面试机会。作为一名软件测试人员&#xff0c;丰富的实战经验是让自己脱颖而出的关键之一。 在我多年从事APP项目软件测试的工作中&#xff0c;我积累了大量的实践经验&#xff0c…

Java 之 String、StringBuffer与StringBuilder 区别

String String 是被 final 修饰的类&#xff0c;不能被继承&#xff1b;String实现了 Serializable 和Comparable接口&#xff0c;表示String支持序列化和可以比较大小&#xff1b;String底层是通过char类型的数据实现的&#xff0c;并且被final修饰&#xff0c;所以字符串的值…

大数据5--spark

1.Spark 定义:Apache Spark是用于大规模数据(large-scala data)处理的统一(unified)分析引擎。 Spark 是什么 Spark 最早源于一篇论文 Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing&#xff0c; 该论文是由加州大学柏克莱…

vue3中其他的变化

1.全局API的转移 Vue 2.x 有许多全局 API 和配置。 - 例如&#xff1a;注册全局组件、注册全局指令等。 //注册全局组件 Vue.component(MyButton, {data: () > ({count: 0}),template: <button click"count">Clicked {{ count }} times.</button> …

微信小程序+wx.connectSocket客服问答

项目需求&#xff0c;记录一下&#xff1a; 1.要求websocket实时返回会话结果 我项目这边是后端一次返回一个字&#xff0c;–finish–结束&#xff0c;所以实现方法是每获取到数据时就setData一次&#xff0c;直到获取到的数据为finish&#xff0c;停止setData 后端返回结果…

Banana Pi BPI-Centi-S3 使用MicroPython编程显示JPG图片

BPI-Centi-S3是我们新推出的一款板载1.9英寸彩屏的小尺寸ESP32-S3开发板&#xff01; BPI-Centi-S3 banana-pi wiki BPI-Centi-S3 bpi-steam wiki 1 关键特性 ESP32-S3&#xff0c;Xtensa 32 bit LX72M PSRAM , 8M FLASH2.4G WIFI &#xff0c;Bluetooth 5 &#xff0c;Blue…

Windows下安装使用Kafka(使用Kafka内置的ZooKeeper)

Windows下安装使用Kafka(使用Kafka内置的ZooKeeper) Kafka2.8版本才开始自带了Zookeeper&#xff0c;所以注意下版本 kafka官网&#xff1a;https://kafka.apache.org kafka配置快速入门&#xff1a;https://kafka.apache.org/quickstart kafka下载页面&#xff1a;https:/…

找出1-1000中的所有完美数

再次练习查找完美数&#xff0c;找出 1-1000 中的所有完美数。 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那么简单…… 地址&#xff1a;https://l…

【LeetCode】剑指 Offer 62. 圆圈中最后剩下的数字(约瑟夫环问题) p300 -- Java Version

题目链接&#xff1a;https://leetcode.cn/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/ 1. 题目介绍&#xff08;62. 圆圈中最后剩下的数字&#xff09; 0,1,,n-1 这n个数字排成一个圆圈&#xff0c;从数字0开始&#xff0c;每次从这个圆圈里删除第m个数字&a…

人工标注或成过去式?SSA语义分割框架、SSA-engine自动类别标注引擎,大幅提升细粒度语义标注效率

推荐语 4月5日&#xff0c;Meta发布 Segment Anything 模型和 SA-1B 数据集&#xff0c;引发CV届“地震”&#xff0c;其凭借一己之力&#xff0c;成功改写了物体检测、数据标注、图像分割等任务的游戏规则。 复旦大学ZVG实验室团队基于此最新开源了SSA语义分割框架和SSA-engin…

javaEE初阶 — Servlet API 详解

文章目录 HttpServlet1 Servlet 的生命周期2 代码示例3 使用 postman 构造请求4 使用 ajax 构造请求 HttpServletRequest1 代码示例 前端如何给后端传参1 通过 GET 里的 query string 传参2 通过 POST 借助 form 表单传参3 通过 json 格式传参 HttpServletResponse1 代码示例1.…

ChatGPT会取代RPA?ta自己可不是这么说的!

先说一个AI热知识&#xff1a;ChatGPT 的推出在科技界引发了一场狂潮。 聊天机器人ChatGPT以及其背后的AI大模型GPT&#xff0c;在2023年引爆全球。GPT 全称为 Generative Pre-trained Transformer&#xff0c;是一种使用人工神经网络的深度学习技术&#xff0c;能够使机器像人…

Transformer and Self-attention

一谈到 NLP&#xff0c;大家都听说过 Transformer&#xff0c; Self-attention 这些词汇&#xff0c;以及 Attension is all you need 这篇论文。 大家可能多多少少看过这类博客&#xff0c;对这些概念有一些了解&#xff0c;什么 QKV呀&#xff0c; encoder&#xff0c; decod…

贪心-刷杂技的牛

题意 农民约翰的 N 头奶牛&#xff08;编号为 1..N&#xff09;计划逃跑并加入马戏团&#xff0c;为此它们决定练习表演杂技。 奶牛们不是非常有创意&#xff0c;只提出了一个杂技表演&#xff1a; 叠罗汉&#xff0c;表演时&#xff0c;奶牛们站在彼此的身上&#xff0c;形成一…

Revit中如何绘制轴线?CAD图纸转轴网操作

一、如何用revit来制作这么一个简单的轴线呢? 01 、新建项目 绘制轴线&#xff0c;首先新建项目建筑样板 02 、轴线快捷键 绘制轴线的快捷键需要牢记&#xff0c;因为经常使用GR 03 、编辑轴线类型 当你画好第一条轴线&#xff0c;需要对轴线类型属性进行调节&#xff0c;一般…

基于vivado(语言Verilog)的FPGA学习(5)——跨时钟处理

基于vivado&#xff08;语言Verilog&#xff09;的FPGA学习&#xff08;5&#xff09;——跨时钟处理 1. 为什么要解决跨时钟处理问题 慢时钟到快时钟一般都不需要处理&#xff0c;关键需要解决从快时钟到慢时钟的问题&#xff0c;因为可能会漏信号或者失真&#xff0c;比如&…