当前位置:首页 > 科技  > 软件

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

来源: 责编: 时间:2023-12-08 09:14:24 167观看
导读设计模式是在软件设计中用于解决常见问题的经过验证的解决方案。设计模式并不是代码或库,而是一种解决问题的思考方式。在使用设计模式时,需要考虑一些基本的设计原则,这些原则有助于构建灵活、可维护和可扩展的软件系统

JDl28资讯网——每日最新资讯28at.com

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

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

它指导我们确保一个类只有一个责任。类的责任应该是单一的,即一个类应该只有一个引起它变化的原因。这有助于提高类的内聚性,使得类更加容易理解、修改和维护。JDl28资讯网——每日最新资讯28at.com

// 违反单一职责原则的例子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 类中,遵循了单一职责原则,使得每个类都更加简单和可维护。这样的设计有助于将系统的不同部分解耦,提高代码的灵活性和可扩展性。JDl28资讯网——每日最新资讯28at.com

一个类应该只有一个引起变化的原因。换句话说,一个类应该只有一个责任。JDl28资讯网——每日最新资讯28at.com

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

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

具体来说,开放/封闭原则的核心思想是:JDl28资讯网——每日最新资讯28at.com

开放(Open):

软件实体应该可以在不修改它的源代码的情况下进行扩展。JDl28资讯网——每日最新资讯28at.com

新功能应该通过添加新代码来实现,而不是通过修改已有代码。JDl28资讯网——每日最新资讯28at.com

封闭(Closed):

已有的软件实体不应该被修改,因为修改可能引入新的错误或影响现有功能的稳定性。JDl28资讯网——每日最新资讯28at.com

这样的设计使得系统更加稳定,因为不需要修改现有代码,只需要添加新的代码。这也有助于降低代码的耦合性,提高代码的可维护性和可扩展性。JDl28资讯网——每日最新资讯28at.com

// 违反开放/封闭原则的例子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 类。这样,系统的扩展性得到了提高,同时保持了对现有代码的封闭性。JDl28资讯网——每日最新资讯28at.com

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

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

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

如果对每一个类型为 S 的对象 o1,都有类型为 T 的对象 o2,使得以 T 定义的所有程序 P 在所有的对象 o1 都替换成 o2 时,程序 P 的行为没有发生变化,那么类型 S 是类型 T 的子类型。JDl28资讯网——每日最新资讯28at.com

换句话说,如果子类型可以替换父类型而不影响程序的正确性,那么这个子类型是符合里氏替换原则的。JDl28资讯网——每日最新资讯28at.com

// 违反里氏替换原则的例子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类,确保子类型可以被替换而不引起意外的行为变化。通过这种方式,程序可以更灵活地使用不同的形状类型,而不必担心替换时可能引发的问题。JDl28资讯网——每日最新资讯28at.com

子类型必须能够替换其基类型而不改变程序的正确性。如果一个类是某个抽象类的子类,那么它应该能够替代该抽象类的任何地方,并且程序的行为不会改变。JDl28资讯网——每日最新资讯28at.com

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

由罗伯特·马丁(Robert C. Martin)提出。该原则主要有两个核心观点:JDl28资讯网——每日最新资讯28at.com

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

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

// 违反依赖倒置原则的例子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 类的实现。这提高了系统的灵活性和可维护性。JDl28资讯网——每日最新资讯28at.com

高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。这促使使用接口或抽象类来减少模块之间的耦合。JDl28资讯网——每日最新资讯28at.com

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

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

接口隔离原则可以通过将大接口拆分成更小的、更具体的接口来实现。具体来说,一个类只应该知道它需要使用的方法,而不需要了解其他不相关的方法。JDl28资讯网——每日最新资讯28at.com

// 违反接口隔离原则的例子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 接口。这避免了类实现不需要的方法,提高了系统的灵活性和可维护性。JDl28资讯网——每日最新资讯28at.com

不应该强迫客户端依赖于它们不使用的接口。一个类不应该被迫实现它用不到的接口。JDl28资讯网——每日最新资讯28at.com

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

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

合成/聚合复用原则的主要原则有两个:JDl28资讯网——每日最新资讯28at.com

优先使用合成(Composition):

通过将对象组合在一起来创建新的对象,而不是通过继承现有类。这样可以更灵活地构建对象的行为,而不会产生继承链的问题。JDl28资讯网——每日最新资讯28at.com

优先使用聚合(Aggregation):

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

// 违反合成/聚合复用原则的例子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 类,而不是通过继承。这降低了类之间的耦合性,使得系统更加灵活,更容易进行复用和维护。使用合成和聚合的方式可以避免继承链的问题,并提高系统的灵活性。JDl28资讯网——每日最新资讯28at.com

首选使用合成/聚合,而不是继承。通过将现有类的实例组合到新的类中,而不是通过继承现有类来实现代码复用。JDl28资讯网——每日最新资讯28at.com

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

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

一个对象应该对其他对象保持最少的了解。只与你的直接朋友通信,而避免和陌生人通信。JDl28资讯网——每日最新资讯28at.com

这意味着一个类应该尽量减少对其他类的引用,尽量减少依赖关系,以降低类之间的耦合度。通过保持对象之间的关系简单,可以提高系统的灵活性和可维护性。JDl28资讯网——每日最新资讯28at.com

// 违反迪米特法则的例子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 类进行间接的交互。这降低了类之间的耦合度,符合迪米特法则的要求。JDl28资讯网——每日最新资讯28at.com

一个软件实体应当尽可能少地与其他实体发生相互作用。也被称为最少知识原则。JDl28资讯网——每日最新资讯28at.com

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

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

本文链接:http://www.28at.com/showinfo-26-39508-0.html设计之魅:高质量面向对象设计的秘密

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 这套分布式IM即时通讯系统如何写到简历上?我给你整理好了!

下一篇: 探索Spring Boot中@PostConstruct的魔法

标签:
  • 热门焦点
  • 一篇聊聊Go错误封装机制

    一篇聊聊Go错误封装机制

    %w 是用于错误包装(Error Wrapping)的格式化动词。它是用于 fmt.Errorf 和 fmt.Sprintf 函数中的一个特殊格式化动词,用于将一个错误(或其他可打印的值)包装在一个新的错误中。使
  • 十个简单但很有用的Python装饰器

    十个简单但很有用的Python装饰器

    装饰器(Decorators)是Python中一种强大而灵活的功能,用于修改或增强函数或类的行为。装饰器本质上是一个函数,它接受另一个函数或类作为参数,并返回一个新的函数或类。它们通常用
  • 一文搞定Java NIO,以及各种奇葩流

    一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 猿辅导与新东方的两种“归途”

    猿辅导与新东方的两种“归途”

    作者|卓心月 出品|零态LT(ID:LingTai_LT)如何成为一家伟大企业?答案一定是对“势”的把握,这其中最关键的当属对企业战略的制定,且能够站在未来看现在,即使这其中的
  • 消费结构调整丨巨头低价博弈,拼多多还卷得动吗?

    消费结构调整丨巨头低价博弈,拼多多还卷得动吗?

    来源:征探财经作者:陈香羽随着流量红利的退潮,电商的存量博弈越来越明显。曾经主攻中高端与品质的淘宝天猫、京东重拾“低价”口号。而过去与他们错位竞争的拼多多,靠
  • 花7万退货退款无门:谁在纵容淘宝珠宝商家造假?

    花7万退货退款无门:谁在纵容淘宝珠宝商家造假?

    来源:极点商业作者:杨铭在淘宝购买珠宝玉石后,因为保证金不够赔付,店铺关闭,退货退款难、维权无门的比比皆是。“提供相关产品鉴定证书,支持全国复检,可以30天无理由退换货。&
  • 小米MIX Fold 3配置细节曝光:搭载领先版骁龙8 Gen2+罕见5倍长焦

    小米MIX Fold 3配置细节曝光:搭载领先版骁龙8 Gen2+罕见5倍长焦

    这段时间以来,包括三星、一加、荣耀等等有不少品牌旗下的最新折叠屏旗舰都得到了不少爆料,而小米新一代折叠屏旗舰——小米MIX Fold 3此前也屡屡被传
  • 三星显示已开始为AR设备研发硅基LED微显示屏

    三星显示已开始为AR设备研发硅基LED微显示屏

    7月18日消息,据外媒报道,随着苹果首款头显产品Vision Pro在6月份正式推出,AR/VR/MR等头显产品也就将成为各大公司下一个重要的竞争领域,对显示屏这一关
  • 机构称Q2全球智能手机出货量同比下滑11% 苹果份额依旧第2

    机构称Q2全球智能手机出货量同比下滑11% 苹果份额依旧第2

    7月20日消息,据外媒报道,研究机构的报告显示,由于需求下滑,今年二季度全球智能手机的出货量,同比下滑了11%,三星、苹果等主要厂商的销量,较去年同期均有下
Top