首页 > 其他分享 >设计之魅:高质量面向对象设计的秘密

设计之魅:高质量面向对象设计的秘密

时间:2024-04-02 17:31:52浏览次数:20  
标签:之魅 原则 width void class 面向对象 设计 height public

设计模式是在软件设计中用于解决常见问题的经过验证的解决方案。设计模式并不是代码或库,而是一种解决问题的思考方式。在使用设计模式时,需要考虑一些基本的设计原则,这些原则有助于构建灵活、可维护和可扩展的软件系统。以下是一些常见的设计原则:

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

它指导我们确保一个类只有一个责任。类的责任应该是单一的,即一个类应该只有一个引起它变化的原因。这有助于提高类的内聚性,使得类更加容易理解、修改和维护。

// 违反单一职责原则的例子
class Report {
    private String title;
    private String content;

    public Report(String title, String content) {
        this.title = title;
        this.content = content;
    }

    public void generateReport() {
        // 生成报告的业务逻辑
        System.out.println("Generating report for " + title + " with content: " + content);
    }

    public void saveToFile() {
        // 将报告保存到文件的业务逻辑
        String filename = title.replace(" ", "_") + ".txt";
        // 实际保存到文件的代码略
        System.out.println("Report saved to " + filename);
    }
}

// 遵循单一职责原则的例子
class Report {
    private String title;
    private String content;

    public Report(String title, String content) {
        this.title = title;
        this.content = content;
    }

    public void generateReport() {
        // 生成报告的业务逻辑
        System.out.println("Generating report for " + title + " with content: " + content);
    }
}

class FileSaver {
    public static void saveToFile(Report report) {
        // 将报告保存到文件的业务逻辑
        String filename = report.getTitle().replace(" ", "_") + ".txt";
        // 实际保存到文件的代码略
        System.out.println("Report saved to " + filename);
    }
}

// 上述例子中,Report 类负责生成报告,而 FileSaver 类负责将报告保存到文件。这样,每个类都有一个清晰的责任,遵循了单一职责原则。

在上述例子中,第一个示例中的 Report 类违反了单一职责原则,因为它负责生成报告和保存报告到文件两个不同的责任。在第二个示例中,将这两个责任分别放在 Report 类和 FileSaver 类中,遵循了单一职责原则,使得每个类都更加简单和可维护。这样的设计有助于将系统的不同部分解耦,提高代码的灵活性和可扩展性。

一个类应该只有一个引起变化的原因。换句话说,一个类应该只有一个责任。

开放/封闭原则(Open/Closed Principle - OCP):

由勃兰特·梅耶(Bertrand Meyer)提出。该原则表明一个软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。简而言之,当需要添加新功能时,应该通过扩展而不是修改现有代码来实现。

具体来说,开放/封闭原则的核心思想是:

  1. 开放(Open):

软件实体应该可以在不修改它的源代码的情况下进行扩展。

新功能应该通过添加新代码来实现,而不是通过修改已有代码。

  1. 封闭(Closed):

已有的软件实体不应该被修改,因为修改可能引入新的错误或影响现有功能的稳定性。

这样的设计使得系统更加稳定,因为不需要修改现有代码,只需要添加新的代码。这也有助于降低代码的耦合性,提高代码的可维护性和可扩展性。

// 违反开放/封闭原则的例子
class Rectangle {
    public double width;
    public double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
}

class AreaCalculator {
    public double calculateRectangleArea(Rectangle rectangle) {
        return rectangle.width * rectangle.height;
    }
}

// 上述代码违反了开放/封闭原则,如果要添加一个新的形状(例如圆形),就需要修改 AreaCalculator 类。

// 遵循开放/封闭原则的例子
interface Shape {
    double calculateArea();
}

class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public double calculateArea() {
        return width * height;
    }
}

class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

class AreaCalculator {
    public double calculateShapeArea(Shape shape) {
        return shape.calculateArea();
    }
}

// 上述代码遵循了开放/封闭原则,通过引入 Shape 接口和不同的形状类,可以轻松地添加新的形状而无需修改 AreaCalculator 类。

在遵循开放/封闭原则的例子中,通过引入一个 Shape 接口和不同的形状类(例如 Rectangle 和 Circle),可以轻松地添加新的形状而无需修改 AreaCalculator 类。这样,系统的扩展性得到了提高,同时保持了对现有代码的封闭性。

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在不修改现有代码的情况下,可以通过添加新的代码来扩展系统的功能。

里氏替换原则(Liskov Substitution Principle - LSP):

由计算机科学家巴巴拉·利斯科夫(Barbara Liskov)提出。该原则指导着子类型(派生类或子类)如何与基类型(基类或父类)进行替换,以确保程序的正确性和一致性。

如果对每一个类型为 S 的对象 o1,都有类型为 T 的对象 o2,使得以 T 定义的所有程序 P 在所有的对象 o1 都替换成 o2 时,程序 P 的行为没有发生变化,那么类型 S 是类型 T 的子类型。

换句话说,如果子类型可以替换父类型而不影响程序的正确性,那么这个子类型是符合里氏替换原则的。

// 违反里氏替换原则的例子

class Rectangle {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int calculateArea() {
        return width * height;
    }
}

class Square extends Rectangle {
    @Override
    public void setWidth(int width) {
        super.setWidth(width);
        super.setHeight(width);
    }

    @Override
    public void setHeight(int height) {
        super.setHeight(height);
        super.setWidth(height);
    }
}

// 上述代码违反了里氏替换原则,因为在Square类中重写了setWidth和setHeight方法,导致Square对象在替换Rectangle对象时可能会引发意料之外的行为。

// 遵循里氏替换原则的例子

class Shape {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int calculateArea() {
        return width * height;
    }
}

class Rectangle extends Shape {
    // 省略特有的方法或属性
}

class Square extends Shape {
    @Override
    public void setWidth(int side) {
        super.setWidth(side);
        super.setHeight(side);
    }

    @Override
    public void setHeight(int side) {
        super.setHeight(side);
        super.setWidth(side);
    }
}

// 上述代码遵循了里氏替换原则,因为Square类继承自Shape类,没有修改基类的行为,而是通过适当的方式扩展了基类的功能。

在遵循里氏替换原则的例子中,Square类不再继承自Rectangle类,而是继承自一个通用的Shape类,确保子类型可以被替换而不引起意外的行为变化。通过这种方式,程序可以更灵活地使用不同的形状类型,而不必担心替换时可能引发的问题。

子类型必须能够替换其基类型而不改变程序的正确性。如果一个类是某个抽象类的子类,那么它应该能够替代该抽象类的任何地方,并且程序的行为不会改变。

依赖倒置原则(Dependency Inversion Principle - DIP):

由罗伯特·马丁(Robert C. Martin)提出。该原则主要有两个核心观点:

  1. 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
  2. 抽象不应该依赖于细节,细节应该依赖于抽象。

在设计系统时,应该通过依赖于抽象而不是具体实现来减少模块之间的耦合。高层模块和低层模块都应该依赖于通用的抽象,而不是彼此直接依赖。这有助于提高系统的灵活性、可维护性和可扩展性。

// 违反依赖倒置原则的例子

class LightBulb {
    public void turnOn() {
        System.out.println("LightBulb: Bulb turned on...");
    }

    public void turnOff() {
        System.out.println("LightBulb: Bulb turned off...");
    }
}

class Switch {
    private LightBulb bulb;

    public Switch() {
        this.bulb = new LightBulb();
    }

    public void operate() {
        if (bulb != null) {
            if (bulb.isOn()) {
                bulb.turnOff();
            } else {
                bulb.turnOn();
            }
        }
    }
}

// 上述代码违反了依赖倒置原则,因为Switch类直接依赖于具体的LightBulb类。

// 遵循依赖倒置原则的例子

interface Switchable {
    void turnOn();

    void turnOff();
}

class LightBulb implements Switchable {
    @Override
    public void turnOn() {
        System.out.println("LightBulb: Bulb turned on...");
    }

    @Override
    public void turnOff() {
        System.out.println("LightBulb: Bulb turned off...");
    }
}

class Switch {
    private Switchable device;

    public Switch(Switchable device) {
        this.device = device;
    }

    public void operate() {
        if (device != null) {
            if (device.isOn()) {
                device.turnOff();
            } else {
                device.turnOn();
            }
        }
    }
}

// 上述代码遵循了依赖倒置原则,Switch类依赖于通用的Switchable接口而不是具体的LightBulb类。

在遵循依赖倒置原则的例子中,Switch 类不再直接依赖于 LightBulb 类,而是依赖于通用的 Switchable 接口。这样,如果有其他类实现了 Switchable 接口,可以轻松地替换 LightBulb 类,而不影响 Switch 类的实现。这提高了系统的灵活性和可维护性。

高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。这促使使用接口或抽象类来减少模块之间的耦合。

接口隔离原则(Interface Segregation Principle - ISP):

接口隔离原则(Interface Segregation Principle - ISP)是面向对象设计中的一个原则,它强调一个类不应该被强迫依赖于它不使用的接口。该原则的目标是防止一个类因为实现了不需要的接口而变得庞大臃肿,降低类的内聚性。

接口隔离原则可以通过将大接口拆分成更小的、更具体的接口来实现。具体来说,一个类只应该知道它需要使用的方法,而不需要了解其他不相关的方法。

// 违反接口隔离原则的例子

interface Worker {
    void work();

    void eat();
}

class Robot implements Worker {
    @Override
    public void work() {
        System.out.println("Robot is working...");
    }

    @Override
    public void eat() {
        // 空实现,机器人无需进食
    }
}

class Human implements Worker {
    @Override
    public void work() {
        System.out.println("Human is working...");
    }

    @Override
    public void eat() {
        System.out.println("Human is eating...");
    }
}

// 上述代码违反了接口隔离原则,因为Robot类实现了不需要的eat方法。

// 遵循接口隔离原则的例子

interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

class Robot implements Workable {
    @Override
    public void work() {
        System.out.println("Robot is working...");
    }
}

class Human implements Workable, Eatable {
    @Override
    public void work() {
        System.out.println("Human is working...");
    }

    @Override
    public void eat() {
        System.out.println("Human is eating...");
    }
}

// 上述代码遵循了接口隔离原则,将大接口拆分成Workable和Eatable两个小接口,类只需要实现它们真正需要的接口。

在遵循接口隔离原则的例子中,将大接口拆分成 Workable 和 Eatable 两个小接口。这样,Robot 类只需实现 Workable 接口,而 Human 类则同时实现了 Workable 和 Eatable 接口。这避免了类实现不需要的方法,提高了系统的灵活性和可维护性。

不应该强迫客户端依赖于它们不使用的接口。一个类不应该被迫实现它用不到的接口。

合成/聚合复用原则(Composition/Aggregation Reuse Principle - CARP):

合成/聚合复用原则(Composition/Aggregation Reuse Principle - CARP)是面向对象设计中的一个原则,它强调在复用时优先使用组合(Composition)和聚合(Aggregation),而不是继承。该原则的核心思想是通过将现有的类组合在一起来创建新的类,而不是通过继承现有类。

合成/聚合复用原则的主要原则有两个:

  1. 优先使用合成(Composition):

通过将对象组合在一起来创建新的对象,而不是通过继承现有类。这样可以更灵活地构建对象的行为,而不会产生继承链的问题。

  1. 优先使用聚合(Aggregation):

聚合是一种特殊的合成关系,表示一种“整体-部分”的关系,但整体和部分之间的生命周期可以独立存在。这允许部分对象在没有整体对象的情况下存在。与合成一样,聚合也提供了更灵活的复用方式。

// 违反合成/聚合复用原则的例子

class Engine {
    public void start() {
        System.out.println("Engine starting...");
    }
}

class Car extends Engine {
    public void drive() {
        System.out.println("Car is driving...");
    }
}

// 上述代码违反了合成/聚合复用原则,因为Car类通过继承Engine类,导致Car和Engine之间形成了紧耦合的关系。

// 遵循合成/聚合复用原则的例子

class Engine {
    public void start() {
        System.out.println("Engine starting...");
    }
}

class Car {
    private Engine engine;

    public Car(Engine engine) {
        this.engine = engine;
    }

    public void drive() {
        engine.start();
        System.out.println("Car is driving...");
    }
}

// 上述代码遵循了合成/聚合复用原则,Car类通过组合引入了Engine类,而不是通过继承。这降低了类之间的耦合性,使得系统更加灵活。

在遵循合成/聚合复用原则的例子中,Car 类通过组合引入了 Engine 类,而不是通过继承。这降低了类之间的耦合性,使得系统更加灵活,更容易进行复用和维护。使用合成和聚合的方式可以避免继承链的问题,并提高系统的灵活性。

首选使用合成/聚合,而不是继承。通过将现有类的实例组合到新的类中,而不是通过继承现有类来实现代码复用。

迪米特法则(Law of Demeter - LoD):

迪米特法则(Law of Demeter - LoD),也被称为最少知识原则,是面向对象设计中的一项原则。迪米特法则强调一个对象应该对其他对象有最少的了解,即一个类不应该直接与其他类过多地发生相互作用。

一个对象应该对其他对象保持最少的了解。只与你的直接朋友通信,而避免和陌生人通信。

这意味着一个类应该尽量减少对其他类的引用,尽量减少依赖关系,以降低类之间的耦合度。通过保持对象之间的关系简单,可以提高系统的灵活性和可维护性。

// 违反迪米特法则的例子

class Teacher {
    public void instruct(Student student) {
        // 教师直接与学生对象发生交互
        student.study();
    }
}

class Student {
    public void study() {
        System.out.println("Student is studying...");
    }
}

// 上述代码违反了迪米特法则,因为Teacher类直接与Student类发生了交互。

// 遵循迪米特法则的例子

class Teacher {
    public void instruct(StudentProxy studentProxy) {
        // 教师只与学生代理对象发生交互,而不直接与学生对象交互
        studentProxy.study();
    }
}

class Student {
    public void study() {
        System.out.println("Student is studying...");
    }
}

class StudentProxy {
    private Student student;

    public StudentProxy(Student student) {
        this.student = student;
    }

    public void study() {
        // 通过代理对象转发请求给学生对象
        student.study();
    }
}

// 上述代码遵循了迪米特法则,Teacher类只与StudentProxy类发生交互,而不直接与Student类发生交互。

在遵循迪米特法则的例子中,Teacher 类只与 StudentProxy 类发生交互,而不直接与 Student 类发生交互。这样,Teacher 类不需要了解 Student 类的内部实现,通过 StudentProxy 类进行间接的交互。这降低了类之间的耦合度,符合迪米特法则的要求。

一个软件实体应当尽可能少地与其他实体发生相互作用。也被称为最少知识原则。

在面向对象设计中,设计原则是指导我们创建灵活、可维护、可扩展软件系统的重要指导方针。每个设计原则都强调特定的方面,例如单一职责原则、开放/封闭原则、里氏替换原则、依赖倒置原则、接口隔离原则和合成/聚合复用原则。这些原则共同构建了一个强大的设计基础,有助于在面对不断变化的需求时更好地应对挑战。

在实际开发中,理解并应用这些设计原则是至关重要的。它们提供了一组指导原则,帮助自己构建出更加健壮和灵活的软件系统。通过不断学习和实践,可以更好地运用这些原则来创建高质量的面向对象设计。

标签:之魅,原则,width,void,class,面向对象,设计,height,public
From: https://blog.csdn.net/yangyufneg/article/details/137278235

相关文章

  • [附源码]JAVA计算机毕业设计电子市场计算机配件报价系统(源码+开题)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着信息技术的快速发展和普及,计算机作为现代人日常生活和工作中的重要工具,其配件市场的需求日益增长。电子市场作为连接供应商与消费者的桥梁,在推动......
  • [附源码]JAVA计算机毕业设计电子商城购物系统(源码+开题)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着互联网技术的迅猛发展和普及,网络购物已成为现代人生活中不可或缺的一部分。电子商城购物系统作为网络购物的重要载体,为企业提供了一个全新的销售......
  • 【附源码】计算机毕业设计音乐豆瓣(java+springboot+mysql+mybatis+论文)
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义音乐豆瓣是一个以音乐为主题的社交网站,用户可以在网站上分享自己喜欢的音乐、评论和推荐音乐作品,还可以与其他用户进行交流和互动。音乐豆瓣的目的是为了让更多的......
  • 【附源码】计算机毕业设计玉龙湾小区网站(java+springboot+mysql+mybatis+论文)
    本系统(程序+源码)带文档lw万字以上  文末可领取本课题的JAVA源码参考系统程序文件列表系统的选题背景和意义玉龙湾小区作为一个大型的综合性社区,拥有众多的住户和商铺。为了更好地满足社区居民的需求,提高社区管理的效率和质量,建立一个专门的网站是非常必要的。这个网站可......
  • 数据库设计规范(三大范式)
    1、第一范式*(确保每列保持原子性)第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库满足第一范式。第一范式的合理遵循需要根据系统给的实际需求来确定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成为一......
  • 面向对象14:static关键字详解
    ackagecom.oop.demo07;publicclassStudent{//staticprivatestaticintage;//静态变量多线程里会用到privatedoublescore;//非静态变量publicstaticvoidmain(String[]args){Students1=newStudent();System.out.p......
  • C语言程序设计—实验报告四
    C语言程序设计—实验报告四一、实验目的1.在熟练掌握if语句和switch语句的基础上,能灵活使用if语句和switch语句进行选择结构的程序设计2.学习调试程序二、实验硬、软件环境Windows计算机、Devc6.0三、实验内容及步骤项目一解题思路首先声明一个字符变量ch使用getchar()函......
  • 54.html+css+js网页设计实例/“企业”酒庄主题介绍/web前端期末大作业/
    一、前言  本实例以“企业”酒庄为主题设计,应用html+css+js、图片轮翻效果、留言板、搜索等,供大家参考。【关注作者|获取更多源码(2000+个Web案例源码)|优质文章】;您的支持是我创作的动力!【点赞收藏博文】,Web开发、课程设计、毕业设计有兴趣的联系我交流分享,3Q!二、网页文......
  • COMP S380F Web应用程序的设计与开发
    COMPS380F集团项目(2024)COMPS380FWeb应用程序的设计与开发集团项目(15%)您需要组成一个最多由4名成员组成的小组,每个成员都应分担类似的工作量。主题:您需要为在线书店实现web应用程序。基本特征(占项目的60%):1.您的web应用程序应满足以下关于网页和功能的基本要求:a.使用讲座和实验室......
  • 【软件工程】详细设计(二)
    这里是详细设计文档的第二部分。前一部分点这里4.学生端模块详细设计学生端模块主要由几个组件构成:学生登录界面,成绩查询界面等界面。因为学生端的功能相对来说比较单一,因此这里只给出两个最重要的功能。图4.1学生端模块流程图4.1学生登录界面模块4.1.1类描述定义......