首页 > 其他分享 >软件设计模式的七大原则

软件设计模式的七大原则

时间:2023-12-06 18:22:18浏览次数:29  
标签:软件设计 double void 七大 模式 class println Override public

1.单一职责原则 一个类应该有且仅有一个引起它变化的原因 例如 实现登录功能,不应该设计一个类,即负责数据库的连接,又负责页面的初始化,又负责数据的接收和处理 而应该把这些功能分开,分成多个不同的类,各司其职

2.开闭原则 一个软件实体应该对拓展开放对修改关闭,也就是说,当需要修改功能或者添加新的功能时,不应该通过修改源代码的方式,而应该通过拓展 举例来说,某一个图形页面提供了各种各样的按钮,例如CircleButton RectangleButton,假设一开始页面使用的是圆形按钮,并且通过一个display方法显示,那如果想换成矩形的按钮,则需要修改图形页面的源代码,这就是违背了对修改关闭

那么如何做才符合开闭原则呢? 可以实现一个抽象按钮类,让各种各样的按钮类,无论是CircleButton、RectangleButton还是其他的,都继承它,在图形页面中只需要修改XML文件即可实现切换,而不需要修改源代码。

3.里氏代换原则

简单来说,代码中任何父类的地方都应该能替换成他的子类,反之则不一定成立,更专业的说法是 任何使用基类对象的地方都可以使用子类对象替换

举例来说

public abstract class Shape {
    public abstract double getArea();
}

public class Rectangle extends Shape {
    private double width;
    private double height;

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

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

public class Circle extends Shape {
    private double radius;

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

    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}
在这个例子中,我们有抽象类 Shape 和它的两个子类:Rectangle 和 Circle。每个形状都有一个获取面积的方法 getArea()。

假设现在有一个场景,我们需要计算一组形状的总面积。我们可以创建一个方法来处理这个问题:

java
public double getTotalArea(Shape[] shapes) {
    double total = 0.0;
    for (Shape shape : shapes) {
        total += shape.getArea();
    }
    return total;
}

那么在主类中 

public static void main(String[] args) {
    Shape[] shapes = new Shape[3];
    shapes[0] = new Rectangle(5, 4);
    shapes[1] = new Circle(3);
    shapes[2] = new Rectangle(2, 6);

    double totalArea = getTotalArea(shapes);
    System.out.println("Total area: " + totalArea);
}

可以把Shape[]中的任意一个对象赋值为它的子类对象

可以看出,里氏替换原则就遵循了开闭原则的对拓展开放对修改关闭

4.依赖倒转原则

针对接口编程,而不是针对实现

举例来说

假设我们有一个Person类,它需要接收消息,最初的设计可能是这样的:

java
class Person {
    private Email email;

    public Person(Email email) {
        this.email = email;
    }

    public void receiveMessage(String message) {
        email.send(message);
    }
}

class Email {
    public void send(String message) {
        // 发送电子邮件的具体实现
    }
}

可以看到,在Person的构造方法中,设置了this.email=email,说明它直接依赖了Email类,并与他进行了耦合

那么假设我现在需要修改,用Wechat接收,那么我需要修改Person的源代码,这显然不符合开闭原则,因此可以这样做

public Abstarct一个接受类型类,存在一个抽象的send方法,让其他的方法,无论是email、phone、wechat类都继承他,实现各自的send方法,然后在Person的构造函数中

直接写 Public Person(AbstractReceive ab),然后再receiveMessage中,使用ab.send(message),这样调用的时候,直接实例化对象,然后注入想要的类型即可

例如在main方法中

Person person = new Person(Wechat wechat);

String message = "用于测试";

person.receiveMessage;

这样就满足了依赖倒转原则

5.接口隔离原则

简单来说,使用多个专门的接口,而不是使用一个单一的总接口,分割的思想

假设我们有一个Animal接口,包含各种动物可能会执行的动作:

java
interface Animal {
    void eat();
    void sleep();
    void fly(); // 飞行方法
}
然后我们创建了两个实现类:Dog和Bird,它们分别实现了Animal接口:

java
class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }

    @Override
    public void sleep() {
        System.out.println("Dog is sleeping");
    }

    @Override
    public void fly() {
        throw new UnsupportedOperationException("Dogs can't fly!");
    }
}

class Bird implements Animal {
    @Override
    public void eat() {
        System.out.println("Bird is eating");
    }

    @Override
    public void sleep() {
        System.out.println("Bird is sleeping");
    }

    @Override
    public void fly() {
        System.out.println("Bird is flying");
    }
}
在这个例子中,Dog类需要实现fly()方法,尽管狗不会飞。这就是违反了接口隔离原则的一个例子,因为Dog类被迫实现了它并不需要的方法。
为了解决这个问题,我们可以将Animal接口拆分为多个更细粒度的接口:

java
interface Eater {
    void eat();
}

interface Sleeper {
    void sleep();
}

interface Flyer {
    void fly();
}

class Dog implements Eater, Sleeper {
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }

    @Override
    public void sleep() {
        System.out.println("Dog is sleeping");
    }
}

class Bird implements Eater, Sleeper, Flyer {
    @Override
    public void eat() {
        System.out.println("Bird is eating");
    }

    @Override
    public void sleep() {
        System.out.println("Bird is sleeping");
    }

    @Override
    public void fly() {
        System.out.println("Bird is flying");
    }
}

7.合成复用原则

多用关联少用继承

假设我们有一个Car类,它需要一个发动机。最初的设计可能是这样的: //Engine是一个抽象类,符合依赖倒转原则

java
class Car {
    private Engine engine;

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

    public void drive() {
        engine.start();
    }
}

class Engine {
    public void start() {
        // 启动发动机的具体实现
    }
}
在这个例子中,Car类通过组合关系包含了一个Engine对象。当我们创建一个新的Car实例时,它会自动拥有一个引擎。这就是合成复用的体现。

如果我们将上述设计改为通过继承实现,可能会像这样:

java
class Car extends Engine {
    public void drive() {
        start();
    }
}

class Engine {
    public void start() {
        // 启动发动机的具体实现
    }
}
在上述例子中,如果Car类继承了Engine类,意味着Car类不仅具有汽车的功能,还承担了发动机的责任。这是因为继承是一种"是-a-kind-of"的关系,即子类代表了一种特殊的父类。在这种情况下,
Car类被看作是一种特殊的Engine,这与现实世界中的概念不符。

 迪米特法则

不要和“陌生人说话”

假设我们有一个订单系统,包括Order、Customer和Product三个类:

java
class Order {
    private Customer customer;
    private List<Product> products;

    public Order(Customer customer, List<Product> products) {
        this.customer = customer;
        this.products = products;
    }

    public double calculateTotalPrice() {
        double totalPrice = 0;
        for (Product product : products) {
            totalPrice += product.getPrice();
        }
        return totalPrice;
    }

    public void deliver() {
        // 调用快递服务将订单送给客户
        // 这里违反了迪米特法则,因为Order类知道太多关于快递服务的知识
    }
}

class Customer {
    private String name;
    private Address address;

    // 构造函数、getter和setter省略...
}

class Product {
    private String name;
    private double price;

    // 构造函数、getter和setter省略...
}

在编写deliver的时候,如果我们直接在其中写快递的方法,就会导致Order了解太多无关的快递服务内容,那么如何避免呢?

定义一个新的类,把deliver方法作为它的方法,在Order中只需要实例化这个新的类并调用方法即可。

标签:软件设计,double,void,七大,模式,class,println,Override,public
From: https://www.cnblogs.com/Arkiya/p/17880219.html

相关文章

  • 在中间件中使用 Scoped 模式服务
    作用域(Scoped)服务和瞬时(Transient)服务都与请求的生命周期相关,但它们之间有一些区别。1.作用域(Scoped)服务:-作用域服务的生命周期与每个请求的生命周期相对应。也就是说,每个请求都会创建一个作用域服务的实例,并且在请求处理过程结束时该实例将被释放。-作用域服务在同一个......
  • 进一步探索智慧医疗新模式,圆心科技上市前景广阔
    在政策、监管的多方推动下,我国智慧医疗形势一片大好,数据显示,2022年我国智医疗应用规模约为780.5亿元,到2023年国内智慧医疗应用规模可达到936.6亿元,未来市场份额也将进一步扩已经成为了众多资本眼中的香饽饽。不久前,我国知名医疗健康公司北京圆心科技就完成了多轮融资,为其赴......
  • VMware桥接模式设置Ubuntu 22固定IP
    Ubuntu22桥接模式下面设置固定IP1、进入netplan网络配置目录cd/etc/netplan修改sudovim00-network-manager-all.yaml我的文件是:01-network-manager-all.yaml2、查看主机的网络信息Windows系统ipconfigLinux系统ifconfig例如主机网络信息: 3、修改配置......
  • 单例模式
    创建单例模式importthreadingimporttimeclassSingleton:instance=None#加锁是为了当多线程调用时,防止出现多线程执行太快都去新建了一个对象,这就违背了单例模式的意义lock=threading.RLock()def__init__(self,name):self.name=nam......
  • 深入理解和实现Qt中的单例模式
    在Qt框架中,单例模式的实现是一个常见的需求,它有多种实现方式,每种都有其特点。以下是对这些方法的探讨,包括宏定义方式、模板类方式,以及Qt的内置单例宏,并附有具体的使用示例。宏定义方式的单例模式宏定义方式是一种传统且直接的实现单例模式的方法,但它可能导致代码重复和类型安全......
  • 小谈设计模式(12)—迪米特法则
    (小谈设计模式(12)—迪米特法则)专栏介绍专栏地址http://t.csdnimg.cn/VpriY专栏介绍主要对目前市面上常见的23种设计模式进行逐一分析和总结,希望有兴趣的小伙伴们可以看一下,会持续更新的。希望各位可以监督我,我们一起学习进步,加油,各位。迪米特法则迪米特法则(LawofDemeter)......
  • 敏捷:应对软件定义汽车时代的开发模式变革
    ​随着软件定义汽车典型应用场景的落地,汽车从交通工具转向智能移动终端的趋势愈发明显。几十年前,一台好车的定义主要取决于高性能的底盘操稳与动力系统;几年前,一台好车的定义主要取决于智能化系统与智能交互能否满足终端用户的用车体验;相信不久后的将来,一台好车的定义将变成:全车传......
  • 设计模式(十六)迭代器
    一、定义提供一种方法顺序访问一个聚合对象中的各个元素,且不用暴露该对象的内部表示。迭代器模式是一种对象行为型模式,又称为游标(Cursor)模式。二、描述在软件系统中,聚合对象拥有两个职责:一是存储数据,二是遍历数据。从依赖性来看,前者是聚合对象的基本职责,而后者既是可变化的,又是......
  • 企业集成模式
    资料作者网站https://www.enterpriseintegrationpatterns.com/patterns/messaging/【金山文档】企业集成模式https://kdocs.cn/l/cfbM5BO6gyYj书第10页SpringIntegrationSpring集成框架概述作为Spring编程模型的延伸,SpringIntegration提供了各种配置选项,包括注......
  • 使用Python代码识别股票价格图表模式
    在股票市场交易的动态环境中,技术和金融的融合催生了分析市场趋势和预测未来价格走势的先进方法。本文将使用Python进行股票模式识别。fromcollectionsimportdefaultdictimportnumpyasnpimportpandasaspdimportmatplotlib.pyplotaspltfromscipy.signalim......