首页 > 其他分享 >SOLID原则介绍和具体使用

SOLID原则介绍和具体使用

时间:2022-11-06 15:25:17浏览次数:62  
标签:return 原则 SOLID sum shape 介绍 int length public

SOLID原则介绍和具体使用

介绍

SOLID是五大面向对象设计原则的缩写。

  1. 单一职责原则(SRP)
  2. 开放封闭原则(OCP)
  3. 里氏替换原则(LSP)
  4. 接口隔离原则(ISP)
  5. 依赖倒置原则(DIP)

初始代码

初始代码有不同的形状shape(圆形,长方形等),累计不同形状面积

shape

圆形

public class Circular {
    private int length;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }
}

长方形

public class Rectangle {
    private int length;

    private int width;

    public int getWidth() {
        return width;
    }

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

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }
}

累积面积方法

参数为List,在for循环中,使用了instanceof方法判断不同的类型,执行不同的面积计算表达式。

public class AreaCalculated {

    public int sum(List<Object> shape){
        int sum = 0;
        for (Object o : shape) {
            if (o instanceof  Circular){
                sum += Math.PI * (((Circular)o).getLength() * ((Circular)o).getLength());
            }
            if (o instanceof Rectangle){
                sum += ((Rectangle) o).getLength() * ((Rectangle) o).getWidth();
            }
        }
        return sum;
    }
}

main

public class Main {
    public static void main(String[] args) {
				// 创建一个圆形对象
        Circular circular = new Circular();
        circular.setLength(10);
				// 创建一个长方形对象
        Rectangle rectangle = new Rectangle();
        rectangle.setLength(10);
        rectangle.setWidth(10);
				// 添加到list中
        List<Object> list = ListUtil.of(circular, rectangle);
				// 累积面积
        AreaCalculated areaCalculated = new AreaCalculated();
        int sum = areaCalculated.sum(list);
			
        System.out.println(sum);
    }
}

准备好上面的代码后,开始solid的原理介绍和具体使用,包括正确使用方法和错误使用方法。

SOLID

单一职责原则(SRP)

单一职责原则(SRP)表名一个类有且只有一个职责。一个类中可以添加任意的属性和方法,如果添加太多,整个类会显比较笨重,还会产生歧义。

现在我们要为计算出来的面积近行打印,打印json格式或者csv格式。

错误示例

public class AreaCalculated {

    public int sum(List<Object> shapes){
        int sum = 0;
        for (Object shape : shapes) {
            if (shape instanceof  Circular){
                sum += Math.PI * (((Circular)shape).getLength() * ((Circular)shape).getLength());
            }
            if (shape instanceof Rectangle){
                sum += ((Rectangle) shape).getLength() * ((Rectangle) shape).getWidth();
            }
        }
        return sum;
    }
    
    public String json(List<Object> shapes){
        return String.format("{sum: %s}", sum(shapes));
    }
}

在AreaCalculated类中添加了一个json方法,用来见面积结果进行打印,这样也能实现,但是却违背了单一职责原则,而且很容易让程序员产生困惑,AreaCalculated类名的语义是计算面积,但是在里面却有一个打印方法,这就很容易产生歧义。在AreaCalculated类中,我们应该添加面积计算的相关方法,比如:计算一个shape的面积,多个shape的面积,两个面积的差等等方法,总之是有关面积方法,不应该添加其他方法。

正确示例

public class ShapePrinter {
    
    public String json(int sum){
        return String.format("{sum: %s}", sum);
    }
    
    public String csv(int sum){
        return String.format("sum,%s",sum);
    }
}

这里新建了一个ShapePrinter打印类,有关shape打印的方法都应该写在这个类中。这样就做到了单一职责原则,也更好理解,不会出现需要打印方法的时候在AreaCalculated类中去找。

开放封闭原则(OCP)

开放封闭原则(OCP)指出,一个类应该对扩展开放,对修改关闭。这意味一旦你创建了一个类并且应用程序的其他部分开始使用它,你不应该修改它。为什么呢?因为如果你改变它,很可能你的改变会引发系统的崩溃。

现在我们新增加一个正方形的面积计算代码。

错误示例

public class Square {
    private int length;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }
}
public class AreaCalculated {

    public int sum(List<Object> shapes){
        int sum = 0;
        for (Object shape : shapes) {
            if (shape instanceof  Circular){
                sum += Math.PI * (((Circular)shape).getLength() * ((Circular)shape).getLength());
            }
            if (shape instanceof Rectangle){
                sum += ((Rectangle) shape).getLength() * ((Rectangle) shape).getWidth();
            }
            if (shape instanceof Square){
                sum += ((Square) shape).getLength() * ((Square) shape).getLength();
            }
        }
        return sum;
    }
}

在代码中新添加了一个Square正方形类,在AreaCalculated.sum()方法的for循环中也新添加关于正方形的面积计算表达式。这样做,同样不会影响代码的正常运行,但是却违反了开放封闭原则(OCP),新增加的正方形修改了sum方法的逻辑,如果继续增加其他的形状,那么就需要一直修改sum方法,修改不当还会导致系统奔溃。

正确示例

新增shape接口

public interface Shape {
    int sum();
}

圆形,实现shape接口

public class Circular implements Shape{
    private int length;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    @Override
    public int sum() {
        return (int) (Math.PI * (getLength() * getLength()));
    }
}

长方形,实现shape接口

public class Rectangle implements Shape {
    private int length;

    private int width;

    public int getWidth() {
        return width;
    }

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

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    @Override
    public int sum() {
        return getLength() * getWidth();
    }
}

正方形,实现shape接口

public class Square implements Shape{
    private int length;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    @Override
    public int sum() {
        return getLength() * getLength();
    }
}

重写面积计算方法

public class AreaCalculated {

    public int sum(List<Shape> shapes){
        int sum = 0;
        for (Shape shape : shapes) {
            sum += shape.sum();
        }
        return sum;
    }
}

main

public class Main {
    public static void main(String[] args) {
        Circular circular = new Circular();
        circular.setLength(10);
        Rectangle rectangle = new Rectangle();
        rectangle.setLength(10);
        rectangle.setWidth(10);
        List<Shape> list = ListUtil.of(circular, rectangle);

        AreaCalculated areaCalculated = new AreaCalculated();
        int sum = areaCalculated.sum(list);

        ShapePrinter printer = new ShapePrinter();
        System.out.println(printer.json(sum));
    }
}

这里将sum方法抽象成一个接口,每个形状都实现这个接口,重写sum方法。这样在AreaCalculated.sum方法就不需要具体实现,只需要掉对应的形状的sum方法即可,这样就能做到无论新增多少个形状,都不需要去修改以前的代码,只需要在原有的代码上继续扩展即可。

里氏替换原则(LSP)

里氏替换原则指出,派生的子类应该是可替换基类的,也就是说任何基类可以出现的地方,子类一定可以出现。值得注意的是,当你通过继承实现多态行为时,如果派生类没有遵守LSP,可能会让系统引发异常。

错误示例

NoShape

public class NoShape implements Shape{
    @Override
    public int sum() {
        throw new RuntimeException();
    }
}

main

public class Main {
    public static void main(String[] args) {
        Circular circular = new Circular();
        circular.setLength(10);
        Rectangle rectangle = new Rectangle();
        rectangle.setLength(10);
        rectangle.setWidth(10);

        NoShape noShape = new NoShape();
        List<Shape> list = ListUtil.of(circular, rectangle, noShape);

        AreaCalculated areaCalculated = new AreaCalculated();
        int sum = areaCalculated.sum(list);

        ShapePrinter printer = new ShapePrinter();
        System.out.println(printer.json(sum));
    }
}

在这里新增了一个NoShape类,并实现Shape接口,实现sum方法,sum方法直接抛出异常。在main方法中,将NoShape添加进list中,运行得到:

Exception in thread "main" java.lang.RuntimeException
	at com.wuguipeng.platform.solid.NoShape.sum(NoShape.java:10)
	at com.wuguipeng.platform.solid.AreaCalculated.sum(AreaCalculated.java:14)
	at com.wuguipeng.platform.solid.Main.main(Main.java:25)

里氏替换原则指出派生的子类应该是可替换基类的,这里NoShape明显不能替换基类,不符合里氏替换原则。

正确示例

NoShape顾名思义,不是形状,那么就不能继承Shape。

接口隔离原则(ISP)

接口隔离原则(ISP)表明类不应该被迫依赖他们不使用的方法,也就是说一个接口应该拥有尽可能少的行为,它是精简的,也是单一的。

错误示例

Shape新增体积计算

public interface Shape {
    int sum();
    
    int volume();
}

正方形,其他几个形状忽略,当是都要实现volume方法

public class Square implements Shape{
    private int length;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    @Override
    public int sum() {
        return getLength() * getLength();
    }

    @Override
    public int volume() {
        return 0;
    }
}

在Shape接口中新增了一个方法,用来计算体积,但是正方形不是正方体,是不计算不出体积的,实现Shape的方法都将强制实现volume方法。这就违背了接口隔离原则。

正确示例

新增体积接口

public interface Volume {
    int volume();
}

三角体

public class TriangularBody implements Shape, Volume{
    @Override
    public int sum() {
        return 0;
    }

    @Override
    public int volume() {
        return 0;
    }
}

正方形

public class Square implements Shape{
    private int length;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    @Override
    public int sum() {
        return getLength() * getLength();
    }

}

在这里新增加了Volume接口并定义了volume方法用了计算体积,如果是一个正方形那么只需要实现Shape接口即可,如果是三角体,那么可以同时实现Shape和Volume接口。

依赖倒置原则(DIP)

依赖倒置原则(DIP)表明高层模块不应该依赖低层模块,相反,他们应该依赖抽象类或者接口。这意味着你不应该在高层模块中使用具体的低层模块。因为这样的话,高层模块变得紧耦合低层模块。如果明天,你改变了低层模块,那么高层模块也会被修改。

错误示例

修改ShapePrinter

public class ShapePrinter {
    
    private final AreaCalculated areaCalculated;

    public ShapePrinter(AreaCalculated areaCalculated) {
        this.areaCalculated = areaCalculated;
    }

    public String json(List<Shape> shapes){
        return String.format("{sum: %s}", areaCalculated.sum(shapes));
    }

    public String csv(List<Shape> shapes){
        return String.format("sum,%s",areaCalculated.sum(shapes));
    }
}

这里修改了ShapePrinter类,在打印的时候才去计算面积,所以注入了AreaCalculated类。这里违法了两个原则,1. 如果修改了AreaCalculated类,那么违法了开放封闭原则(OCP),因为我们强依赖AreaCalculated类并且是个具体的类,不是抽象的,修改可能会导致ShapePrinter遭到破坏。2. ShapePrinter依赖具体的AreaCalculated,而不是抽象的,违法了依赖倒置原则(DIP)。

正确示例

新增IAreaCalculated接口

public interface IAreaCalculated {
    int sum(List<Shape> shapes);
}

实现接口

public class AreaCalculated implements IAreaCalculated {

    @Override
    public int sum(List<Shape> shapes){
        int sum = 0;
        for (Shape shape : shapes) {
            sum += shape.sum();
        }
        return sum;
    }
}

重写ShapePrinter

public class ShapePrinter {

    private final IAreaCalculated iAreaCalculated;

    public ShapePrinter(IAreaCalculated iAreaCalculated) {
        this.iAreaCalculated = iAreaCalculated;
    }

    public String json(List<Shape> shapes){
        return String.format("{sum: %s}", iAreaCalculated.sum(shapes));
    }

    public String csv(List<Shape> shapes){
        return String.format("sum,%s",iAreaCalculated.sum(shapes));
    }
}

main

public class Main {
    public static void main(String[] args) {
        Circular circular = new Circular();
        circular.setLength(10);
        Rectangle rectangle = new Rectangle();
        rectangle.setLength(10);
        rectangle.setWidth(10);
        
        List<Shape> list = ListUtil.of(circular, rectangle);

				// 具体实现
        IAreaCalculated areaCalculated = new AreaCalculated();

        ShapePrinter printer = new ShapePrinter(areaCalculated);
        System.out.println(printer.json(list));
    }
}

这里ShapePrinter依赖IAreaCalculated接口,不依赖具体实现,在main方法中,将具体实现创建出来,传递给ShapePrinter即可。如果要修改AreaCalculated,只需要重新新建一个类实现IAreaCalculated接口,满足开放封闭原则(OCP)原则,同时满足依赖倒置原则(DIP),不依赖具体实现,依赖接口。

标签:return,原则,SOLID,sum,shape,介绍,int,length,public
From: https://www.cnblogs.com/wuguipeng/p/16862640.html

相关文章

  • redis介绍和安装
    redis介绍redis是一种在内存进行存储数据的数据库,当然,它也支持将数据存储到硬盘上redis的存储方式是通过key-value的形式,value可以是很多数据类型,分别是:string(字符串)、li......
  • 我看谁还不懂多线程之间的通信+基础入门+实战教程+详细介绍+附源码
    一、多线程之间的通信(Java版本)1、多线程概念介绍多线程概念在我们的程序层面来说,多线程通常是在每个进程中执行的,相应的附和我们常说的线程与进程之间的关系。线程与进程的......
  • 代码规范和编码原则
    (一)代码规范1.代码风格规范,主要是文字上的规定;2.代码设计规范,牵涉到程序设计、模块之间的关系、设计模式等方方面面的通用原则。(二)代码风格规范代码风格的原则是:简明......
  • NFS常用挂载参数介绍
    NFS常用挂载参数介绍soft/hard 软挂载方式挂载系统,若NFS请求超时,则客户端向调用程序返回错误;如果使用硬连接方式则客户端一直重新请求直至成功。默认为hard ......
  • 23种设计模式-工厂方法模式介绍加实战代码
    1、描述工厂方法模式是对简单工厂模式的抽象提取。有一个抽象的Factory类(可以是抽象类和接口),这个类将不再负责具体的产品生产,而是只制定一些规范,具体的生产工作由其子类......
  • SAP Java Connector 组件介绍
    SAPJavaConnector3.1运行时环境由两部分组成:sapjco3.jar-包含JCo的Java运行时类的存档{libraryprefix}sapjco3{sharedlibraryextension}-包含JCo原生......
  • LAMP介绍与基本配置
    一、LAMP概述1、lamp介绍​LAMP架构是目前成熟的企业网站应用模式之一,指的是协同工作的一整套系统和相关软件,能够提供动态web站点服务及其应用开发环境LAMP是一个缩写词,具体......
  • solidity数组的使用
    长度固定的数组uint[]定义的数组,可以使用引用查看索引位置的数值,使用.length得到数组的长度byte定义的数组,将字符串以十六进制形式保存,不能使用.length。byte默认值为0......
  • UML建模语言、设计原则、设计模式
    1、UML统一建模语言定义:用于软件系统设计与分析的语言工具目的:帮助开发人员更好的梳理逻辑、思路学习地址:UML概述_w3cschool官网:https://www.omg.org/spec/UML1.1......
  • Logstash 入门实战(5)--output plugin 介绍
    本文主要介绍Logstash的一些常用输出插件;相关的环境及软件信息如下:CentOS 7.9、Logstash8.2.2。1、Stdout输出插件Stdout插件把结果数据输出到标准输出。input{......