设计模式--设计模式六大原则

本文围绕Java设计模式的六大原则展开。介绍了单一职责、里式替换、接口隔离、依赖倒置、迪米特法则和开放封闭原则,阐述了各原则的含义、实现方法、优缺点及应用场景,强调这些原则能提升系统扩展性和可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java中的设计模式基本上都是围绕着这六大原则进行设计,设计模式在我们开发过程中时必不可少的,它能够让我们更好的设计一个系统,使得这个系统具有良好的扩展性,可维护性等。下面我们开始了解六大原则!

单一职责原则(Single Responsibility Principle,SRP)

单一职责原则我们可以从名字上了解,就是指一个类的功能要单一,不能包罗万象。正如一个人,分配的工作如果太多,并不会提高工作的效率,有时候甚至会顾此失彼。单一职责原则并非面向对象编程思想所特有,只要是模块化 程序设计,都适用单一职责原则。

实现单一职责原则的方法很简单,加入类A有两个职责P1和P2,那么只需要分别创建两个类C1和C2,让他们分别负责P1和P2。这样就实现了单一职责原则,当我们需要修改P1职责时就只需要修改C1,而不用去修改C2,同时如果C1发生故障也不会影响到C2。

虽然单一职责原则十分简单,但是却十分重要。在实际开发中,随着程序功能的开发,程序的职责随之增加,为了避免程序中某个职责故障导致影响其他的职责运行,单一职责原则必不可少。

使用单一职责原则的优点:

  1. 可以降低类的复杂度,因为一个类只负责一个职责,逻辑肯定比负责多项职责的简单。
  2. 提高了类的可读性,提高系统的可维护性。
  3. 降低系统耦合性,当你修改一个职责时对其他职责的影响能显著降低。

单一职责的缺点是会增加类的数量,但是对于系统的稳定性和可维护性来说,这个缺点几乎可以忽略。

里式替换原则(the Liskov Substitution Principle,LSP)

里式替换原则简单来说就是:子类应当可以替换父类并且出现在父类能够出现的所有地方。例如,一个公司开年会举行员工抽奖活动,那么无论是老员工还是新员工,无论员工的岗位是否一样,只要是这个公司的员工,就可以参加。

里式替换原则依赖于面向对象的继承和多态。继承给我们带来巨大便利的同时,也带来了弊端,例如类的耦合性降低,打破了类的封装等。继承其实还包含了一层含义,父类中凡是实现好的方法,虽然不强制子类必须遵循父类中方法的实现方式,但是如果这些子类对这些非抽象方法任意修改,就失去了继承的意义(如果继承一个类,可是子类完全修改了父类所有的方法,使得子类与父类的相似性近乎于零,那么这样与没有继承有什么区别呢),会对整个继承体系造成破坏。

因此里式替换原则其实还包含了以下几层含义:

  1. 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。(可能造成功能实现方式错误)
  2. 子类可以增加自己特有的方法。
  3. 当子类的方法重载父类的方法时,方法的形参应该比父类方法的输入参数更宽松。
  4. 当子类的方法实现父类的抽象方法时,方法的返回值要比父类更加严格。

接口隔离原则(the Interface Segregation Principle,ISP)

接口隔离原则就是模块间通过抽象接口隔离开,而不是通过具体的类强偶合起来,即面向接口编程。

类间的依赖关系应该建立在最小的接口上,建立单一的接口,而不是建立庞大臃肿的接口,尽量细化接口,减少接口中的方法。我们举例来说明接口隔离原则。

这个图的意思是:类B依赖接口C中的方法1、方法2、方法3,类D是对类B依赖的实现。类A依赖接口I中的方法4、方法5,类C是对类A依赖的实现。但是我们可以很容易的发现一个问题,类C和类D都存在他们不用到的方法。显然这不是一个好的设计,如果要将这个设计修改为复合接口隔离原则,就必须对接口进行拆分,如下图所示:

通过这个例子,我们可以很容易的理解接口隔离原则,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。很多人会觉得接口隔离原则和单一职责原则很相似,但单一职责原则关注的重点是职责,而接口隔离原则关注的重点是接口依赖的隔离。单一职责原则主要是用来约束类,其次才是接口和方法,它关注程序中的实现和细节。而接口隔离原则主要约束接口,针对的是抽象,是对程序整体框架的构建。

采用接口隔离原则有以下几点需要注意:

  1. 接口尽量小,但要有一定限度,过小的话会造成接口数量过多,使得设计复杂化。
  2. 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。

依赖倒置原则(the Dependency Inversion Principle,DIP)

依赖倒置是最难以实现的原则,它是实现开闭原则的重要途径,DIP没有实现就不可能实现开闭原则。首先我们要先对依赖倒置原则中的一些概念进行了解:

  • 低层模块:不可分割的原子逻辑,可能会根据业务逻辑经常变化。
  • 高层模块:低层模块的再组合,对低层模块的抽象。
  • 抽象: 接口或抽象类(是底层模块的抽象,特点:不能直接被实例化)
  • 与接口或抽象类对应的实现类:低层模块的具体实现(特点:可以直拉被实例化)

依赖倒置原则可以概括成两句话:

  1. 高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象
  2. 抽象不应该依赖于具体实现,具体实现应该依赖于抽象

通俗来讲,依赖倒置的本质就是通过抽象(接口或抽象类)使各个类或模块实现彼此相互独立,互不影响,实现模块的松耦合。

依赖倒置的优点

  1. 可以通过抽象使各个类或模块彼此独立,不互相影响,实现模块间的松耦合(本质)
  2. 可以规避一些非技术因素引起的问题,可以减少需求变化的工作量剧增的情况。
  3. 可以促进并行开发。

有一个最常见的例子,就是数据访问抽象层。

在我们编写业务代码时,我们不应该直接在某个业务代码处直接用jdbc操作数据库,业务层方法的参数应该是通过数据访问层的抽象接口进行访问。这个数据访问抽象层就体现了依赖倒置原则。

 

迪米特法则(Law of Demeter,LoD)

迪米特法则的核心思想就是类间解耦合,简单来说就是一个类对自己依赖的类知道的越少越好。什么是耦合,耦合就是对象之间的依赖性,依赖性越强耦合度越高。耦合度高意味着当一个类发生改变时,对与其耦合度高的另一个类产生的影响越大。这会大大提高我们的维护成本。因此对象的设计应该使类和构件之间的耦合最小。

我们通过一个违反迪米特法则的例子来加深我们对其的理解:

 //总公司员工
    class Employee{
        private String id;
        public void setId(String id){
            this.id = id;
        }
        public String getId(){
            return id;
        }
    }

    //分公司员工
    class SubEmployee{
        private String id;
        public void setId(String id){
            this.id = id;
        }
        public String getId(){
            return id;
        }
    }

    class SubCompanyManager{
        public List getAllEmployee(){
            List list = new ArrayList();
            for(int i=0; i<100; i++){
                SubEmployee emp = new SubEmployee();
                //为分公司人员按顺序分配一个ID
                emp.setId("分公司"+i);
                list.add(emp);
            }
            return list;
        }
    }

    class CompanyManager{

        public List getAllEmployee(){
            List list = new ArrayList();
            for(int i=0; i<30; i++){
                Employee emp = new Employee();
                //为总公司人员按顺序分配一个ID
                emp.setId("总公司"+i);
                list.add(emp);
            }
            return list;
        }

        public void printAllEmployee(SubCompanyManager sub){
            List list1 = sub.getAllEmployee();
            for(SubEmployee e:list1){
                System.out.println(e.getId());
            }

            List list2 = this.getAllEmployee();
            for(Employee e:list2){
                System.out.println(e.getId());
            }
        }
    }

    public class Client{
        public static void main(String[] args){
            CompanyManager e = new CompanyManager();
            e.printAllEmployee(new SubCompanyManager());
        }
    }

这个设计的问题主要在于CompanyManager中,从逻辑上总公司只与它的分公司耦合即可,与分公司的员工并无联系,而CompanyManager中却与分公司的员工耦合,这是不必要的耦合,按照迪米特法则我们应该把它消除。

class SubCompanyManager{
        public List getAllEmployee(){
            List list = new ArrayList();
            for(int i=0; i<100; i++){
                SubEmployee emp = new SubEmployee();
                //为分公司人员按顺序分配一个ID
                emp.setId("分公司"+i);
                list.add(emp);
            }
            return list;
        }
        public void printEmployee(){
            List list = this.getAllEmployee();
            for(SubEmployee e:list){
                System.out.println(e.getId());
            }
        }
    }

    class CompanyManager{
        public List getAllEmployee(){
            List list = new ArrayList();
            for(int i=0; i<30; i++){
                Employee emp = new Employee();
                //为总公司人员按顺序分配一个ID
                emp.setId("总公司"+i);
                list.add(emp);
            }
            return list;
        }

        public void printAllEmployee(SubCompanyManager sub){
            sub.printEmployee();
            List list2 = this.getAllEmployee();
            for(Employee e:list2){
                System.out.println(e.getId());
            }
        }
    }

修改后,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。

迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个"中介"来发生联系。但是过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。

开放封闭原则(Open-Close Principle,OCP)

开放封闭原则是面向对象中最重要的设计原则,它不针对具体,而是针对一个思想,一个方向。在任何一个软件项目中,需求是不可能不变的,而面向对象软件框架的设计,就是为了将功能模块封装、降低耦合。而开闭原则就是这个目标的直接体现。其他的设计原则,都是在这一大的原则下进行的。

开放封闭就是一个模块在扩展性方面应该是开放的,而在更改性方面应该是封闭的。也就是在设计一个模块时,应当使这个模块可以在不被修改的前提下被扩展,即可以在不必修改代码的情况下修改这个模块的行为。开放封闭原则目的在于面对需求的改变而能够保持系统的相对稳定。

开放封闭原则其实很难实现,又很容易实现。为什么说他难又说他容易呢?要实现开放封闭原则,意味着你必须实现了其他五个设计原则。当你实现了五个设计原则后,开放封闭原则则自然实现了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值