软件设计原则

文章目录

    • 1、开闭原则
    • 2、里式替换原则
    • 3、依赖倒转原则
    • 4、接口隔离原则
    • 5、迪米特法则

        在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。

1、开闭原则

  1. 对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级
  2. 想要达到这样的效果,我们需要使用接口抽象类
  3. 因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了

例子:搜狗输入法的皮肤设计
分析:搜狗输入法的皮肤是输入法背景图片、窗口颜色和声音等元素的组合,用户可以根据自己的喜爱更换自己的输入法的皮肤,这些皮肤有共同的特点,可以为其定义一个抽象类(AbstractSkin),而每个具体的皮肤(DefaultSkin、MySkin)都是它的子类,这样用户就可以根据需要选择或者新增的主题,而不需要修改源代码,所以它是满足
开闭原则

代码:

public abstract class AbstractSkin {
    // 显示的方法
    public abstract void display();
}
public class DefaultSkin extends AbstractSkin{
    @Override
    public void display() {
        System.out.println("默认皮肤");
    }
}
public class MySkin extends AbstractSkin{
    @Override
    public void display() {
        System.out.println("我的皮肤");
    }
}

DefaultSkin和MySkin是AbstractSkin子类

public class SougouInput {
    private AbstractSkin abstractSkin;
    public void setAbstractSkin(AbstractSkin abstractSkin) {
        this.abstractSkin = abstractSkin;
    }
    public void display(){
        abstractSkin.display();
    }
}
public class Main {
    public static void main(String[] args) {
        SougouInput sougouInput = new SougouInput();
        DefaultSkin defaultSkin = new DefaultSkin();
        MySkin mySkin = new MySkin();
        sougouInput.setAbstractSkin(mySkin);
        sougouInput.display();
    }
}

然后调用的时候,通过SougouInput的set方法,然后再调用display就可以实现多态了,如果想在加一个皮肤的话,只需要再实现AbstractSkin,然后调用即可(但是这里new还是操纵在我们的手里)

2、里式替换原则

  1. 里氏代换原则是面向对象设计的基本原则之一
  2. 里氏代换原则: 任何基类可以出现的地方,子类一定可以出现。通俗理解: 子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

例子:正方形不是长方形
在数学领域中,正方形毫无疑问是长方形,它是一个长宽相等的长方形,所以我们开发的一个与几何图形相关的软件系统,就可以让正方形继承长方形

软件设计原则_第1张图片

代码:

public class Rectangle {
    private double length;
    private double width;

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

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

    public double getLength() {
        return length;
    }

    public double getWidth() {
        return width;
    }
}

基类长方形

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

    @Override
    public void setLength(double length) {
        super.setLength(length);
        super.setWidth(length);
    }
}

正方形继承长方形

public class RectangleDemo {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.setWidth(10);
        rectangle.setLength(20);
        resize(rectangle);
        printWidthAndLength(rectangle);
        System.out.println("=======");
        Square square = new Square();
        square.setLength(10);
        resize(square);
        printWidthAndLength(square);
    }

    public static void resize(Rectangle rectangle){
        while(rectangle.getWidth() <= rectangle.getLength()){
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }

    public static void printWidthAndLength(Rectangle rectangle){
        System.out.println("rectangle = " + rectangle.getLength());
        System.out.println("width = " + rectangle.getWidth());
    }
}

此时我们运行这个方法,======之前的会打印出来,后面的打印不出来,原因是因为Square中的setWidth重写了父类的setWidth,导致长和宽一直要保持相同的,它重写了父类的方法,导致了出错,也就是说正常性不是长方形,这个继承不满足里式替换原则

改进方法:
软件设计原则_第2张图片

代码:

public interface Quadrilateral {
    double getLength();
    double getWidth();
}

接口

public class Rectangle implements Quadrilateral{
    private double length;
    private double width;
    @Override
    public double getLength() {
        return length;
    }

    @Override
    public double getWidth() {
        return width;
    }

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

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

长方形

public class Square implements Quadrilateral{
    private double side;
    @Override
    public double getLength() {
        return side;
    }

    @Override
    public double getWidth() {
        return side;
    }

    public void setSide(double side) {
        this.side = side;
    }

    public double getSide() {
        return side;
    }
}

正方形

public class RectangleDemo {
    public static void main(String[] args) {
        Rectangle rectangle = new Rectangle();
        rectangle.setLength(10);
        rectangle.setWidth(20);
        resize(rectangle);
        printWidthAndLength(rectangle);
        System.out.println("========");
        Square square = new Square();
        square.setSide(10);
        printWidthAndLength(square);
    }

    public static void resize(Rectangle rectangle){
        while(rectangle.getWidth() <= rectangle.getLength()){
            rectangle.setWidth(rectangle.getWidth() + 1);
        }
    }

    public static void printWidthAndLength(Quadrilateral quadrilateral){
        System.out.println("rectangle = " + quadrilateral.getLength());
        System.out.println("width = " + quadrilateral.getWidth());
    }
}

这样的话,解决了刚才的问题(说实话,个人觉得接口比抽象类用的场景多,很少见过抽象类的)

3、依赖倒转原则

       高层模块应该依赖低层模块,两者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象,简单的来说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
软件设计原则_第3张图片

例:组装电脑
现要组装一台电脑,需要配件cpu,硬盘,内存条,只用这些配置都有了,计算机才能正常的运行。

软件设计原则_第4张图片
Before:

public class XijieHardDisk {

    public void save(String data){
        System.out.println("使用希捷存储" + data);
    }

    public String get(){
        System.out.println("使用希捷获取数据");
        return "数据";
    }
}
public class KingStoneMemory {
    public void save(){
        System.out.println("使用金士顿内存条");
    }
}
public class IntelCpu {
    public void run(){
        System.out.println("使用Intel处理器");
    }
}
public class Computer {
    private XijieHardDisk xijieHardDisk;
    private IntelCpu intelCpu;
    private KingStoneMemory kingStoneMemory;
    
    // get/set方法
    public void run(){
        System.out.println("运行计算机");
        String s = xijieHardDisk.get();
        System.out.println("从硬盘上获取的数据是" + s);
        intelCpu.run();
        kingStoneMemory.save();
    }
}
public class ComputerDemo {
    public static void main(String[] args) {
        Computer computer = new Computer();
        XijieHardDisk xijieHardDisk = new XijieHardDisk();
        IntelCpu intelCpu = new IntelCpu();
        KingStoneMemory kingStoneMemory = new KingStoneMemory();
        computer.setIntelCpu(intelCpu);
        computer.setKingStoneMemory(kingStoneMemory);
        computer.setXijieHardDisk(xijieHardDisk);
        computer.run();
    }
}

      从上面看我们已经组装好了一台电脑,但是这台电脑似乎有些不太理想,硬盘、内存、cpu都是写死的了,我们应该设计成想装什么配件就装什么配件,所以要根据依赖倒转原则进行改进:
软件设计原则_第5张图片

public interface Memory {
    public void save();
}
public interface HardDisk {
    public void save(String data);
    public String get();
}
public interface Cpu {
    public void run();
}

这是三个接口,然后XijieHarddDisk、IntelCpu、KingStonMemory去继承他们

public class Computer {
    private HardDisk disk;
    private Cpu cpu;
    private Memory memory;
	// get/set方法
    public void run(){
        System.out.println("运行计算机");
        String s = disk.get();
        System.out.println("从硬盘上获取的数据是" + s);
        cpu.run();
        memory.save();
    }
}

使用接口去作为Computer的属性

public class ComputerDemo {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.setCpu(new IntelCpu());
        computer.setDisk(new XijieHardDisk());
        computer.setMemory(new KingStoneMemory());
        computer.run();
    }
}

      最后使用的时候,通过set实现那些抽象的具体类,也就是你想装什么配置就放什么,这样就很合理喽!

4、接口隔离原则

客户端不应该被迫依赖于它不使用的方法,一个类对另一个的依赖应该建立在最小的接口上

例:我们需要创建一个A品牌的安全门,该安全门具有防火、防水、防盗的功能,可以将防火、防水、防盗功能提取成为一个接口

软件设计原则_第6张图片
上面的设计我们发现了它存在的问题,如果我们需要再创建一个B品牌的安全门,该安全门只具有防盗、防水的功能,如果实现SafetyDoor接口就违背了接口隔离原则,下面是修改优化
软件设计原则_第7张图片
将防盗门的功能都抽取成最小接口,哪个防盗门要实现哪些就实现哪些。

5、迪米特法则

       迪米特法则又叫最小知识原则。只和你的直接朋友交谈,不跟陌生人说话,含义就是如果两个软件实体无须直接通信,那么就不应该发生直接的相互调用,可以通过第三方转发该调用,目的是为了降低类之间的耦合性,提高模块的相对独立性。
      迪米特法则中的朋友是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合和组合关系,可以直接访问这些对象的方法。
软件设计原则_第8张图片

例:明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如和粉丝的见面会,和媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则。

软件设计原则_第9张图片

public class Star {
    private String name;
    public Star(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
public class Fans {
    private String name;
    public Fans(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
public class Company {
    private String name;
    public Company(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
public class Agent {
    private Star star;
    private Fans fans;
    private Company company;
    public void setStar(Star star) {
        this.star = star;
    }
    public void setCompany(Company company) {
        this.company = company;
    }
    public void setFans(Fans fans) {
        this.fans = fans;
    }
    public void meeting(){
        System.out.println(fans.getName() + "与明星" + star.getName() + "见面了");
    }
    public void business(){
        System.out.println(company.getName() + "与明星" + star.getName() + "谈业务");
    }
}
public class Demo {
    public static void main(String[] args) {
        Agent agent = new Agent();
        agent.setFans(new Fans("zzz"));
        agent.setStar(new Star("lzy"));
        agent.setCompany(new Company("iii"));
        agent.meeting();
        agent.business();
    }
}

      从类图中我们可以看出,粉丝、公司和明星如果要进行互动的话,要通过经纪人去调用,也就是只能通过经纪人这个朋友去做调用中介,符合迪米特法则。

你可能感兴趣的:(设计模式,java,开发语言)