3.1软件设计原则

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

3.2开闭原则

对扩展开放,对修改关闭。在程序需要进行扩展的时候,不能去修改原有的代码,实现一个热拔插的效果(在不修改原有代码的基础上能自由的给系统添加新的模块),简言之,是为了使程序的扩展性好,易于维护和升级。

如何达到开闭原则的要求?使用接口和抽象类。

因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生出来的实现类进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展即可。

搜狗输入法的皮肤为例介绍开闭原则的应用

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

案例的Java代码具体实现

抽象皮肤类

public abstract class AbstractSkin {//显示的方法public abstract void display();
}

继承抽象皮肤的两个具体类,默认皮肤和我的皮肤

public class DefaultSkin extends AbstractSkin{@Overridepublic void display() {System.out.println("默认皮肤的显示方法");}
}
public class MySkin extends AbstractSkin{@Overridepublic void display() {System.out.println("自定义皮肤的显示方法");}
}

SougouInput类

public class SougouInput {private AbstractSkin skin;public void setSkin(AbstractSkin skin) {this.skin = skin;}public void display(){skin.display();}
}

Client客户端

public class Client {public static void main(String[] args) {//1、创建搜狗输入法对象SougouInput input=new SougouInput();//2、创建默认皮肤对象DefaultSkin skin=new DefaultSkin();//3、将皮肤设置为默认皮肤并展示方法input.setSkin(skin);input.display();//4、创建自己的皮肤,并设置,并展示方法MySkin mySkin=new MySkin();input.setSkin(mySkin);input.display();}
}

终端输出结果

默认皮肤的显示方法
自定义皮肤的显示方法Process finished with exit code 0

3.3里氏代换原则

里氏代换原则是面向对象设计的基本原则之一。

里氏代换原则:任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。通俗的讲:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

为什么尽量不要重写父类的方法完成新的功能?因为虽然重写父类的方法增加新的功能这样写很简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。

举个栗子

正方形不是长方形

在数学领域中,正方形毫无疑问是长方形,它是一个长宽相等的长方形。所以,开发一个与几何图形相关的软件系统,就可以顺理成章的让正方形继承长方形。

案例具体的Java代码实现

public class Rectangle {private double length;private double width;public double getLength() {return length;}public void setLength(double length) {this.length = length;}public double getWidth() {return width;}public void setWidth(double width) {this.width = width;}
}public class Square extends Rectangle{@Overridepublic void setLength(double length) {super.setLength(length);super.setWidth(length);}@Overridepublic void setWidth(double width) {super.setWidth(width);super.setLength(width);}
}public class RectangleDemo {public static void main(String[] args) {//创建长方形对象Rectangle r=new Rectangle();//设置长和宽r.setWidth(10);r.setLength(20);//调用resize方法resize(r);printLengthAndWidth(r);System.out.println("------");//创建正方形对象Square s=new Square();s.setLength(10);resize(s);printLengthAndWidth(s);}//扩展方法public static void resize(Rectangle rectangle){//判断宽如果比长小,进行扩宽操作while (rectangle.getWidth()<=rectangle.getLength()){//如果没有+1会一直在循环体中无法跳出rectangle.setWidth(rectangle.getWidth()+1);}}//打印长和宽public static void printLengthAndWidth(Rectangle rectangle){System.out.println("长:"+rectangle.getLength());System.out.println("宽:"+rectangle.getWidth());}
}

运行一下这段代码就会发现,假如我们把一个普通长方形作为参数传入resize方法,就会看到长方形宽度逐渐增长的效果,当宽度大于长度,代码就会停止,这种行为的结果符合我们的预期;假如我们再把一个正方形作为参数传入resize方法后,就会看到正方形的宽度和长度在不断增长,代码会一直运行下去,直至系统产生溢出错误。所以普通的长方形是适合这段代码的,正方形不适合。

结论:在resize方法中,Rectangle类型的参数是不能被Square类型的参数所代替,如果进行了替换就得不到预期的结果,因此Square类和Rectangle类之间的继承关系违反了里氏代换原则,它们之间的继承关系不成立,正方形不是长方形。

对上面的错误进行改进,此时我们需要重新设计它们之间的关系,抽象出来一个四边形接口(Quadrilateral),让Rectangle和Square实现Quadrilateral接口

具体实现的Java代码

//四边形接口
public interface Quadrilateral {double getLength();double getWidth();
}public class Square implements Quadrilateral{private double side;public double getSide() {return side;}public void setSide(double side) {this.side = side;}@Overridepublic double getLength() {return this.side;}@Overridepublic double getWidth() {return this.side;}
}public class Rectangle implements Quadrilateral{private double length;private double width;public void setLength(double length) {this.length = length;}public void setWidth(double width) {this.width = width;}@Overridepublic double getLength() {return this.length;}@Overridepublic double getWidth() {return this.width;}
}public class RectangleDemo {public static void main(String[] args) {Rectangle r=new Rectangle();r.setLength(20);r.setWidth(10);resize(r);printLengthAndWidth(r);}public static void resize(Rectangle rectangle){while(rectangle.getWidth()<=rectangle.getLength()){rectangle.setWidth(rectangle.getLength()+1);}}public static void printLengthAndWidth(Quadrilateral quadrilateral){System.out.println(quadrilateral.getLength());System.out.println(quadrilateral.getWidth());}
}结果
20.0
21.0Process finished with exit code 0

3.4依赖倒转原则

高层模块不应该依赖底层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。简单地说就是要对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

例子:组装电脑,组装一台电脑,需要配件cpu、硬盘、内存条。只有这些配置都有了,计算机才能正常地运行。选择cpu有很多选择,如Intel、AMD等,硬盘可以选择希捷,西数等,内存条可以选择金士顿、海盗船等。

public class XiJieHardDisk {public void save(String data){System.out.println("使用希捷影片那存储数据为"+data);}public String get(){System.out.println("使用希捷硬盘接受数据");return "数据";}
}public class KingstinMemory {public void save(){System.out.println("使用金士顿内存条");}
}public class IntelCpu {public void run(){System.out.println("使用Intel处理器");}
}public class Computer {private XiJieHardDisk hardDisk;private IntelCpu cpu;private KingstinMemory memory;public XiJieHardDisk getHardDisk() {return hardDisk;}public void setHardDisk(XiJieHardDisk hardDisk) {this.hardDisk = hardDisk;}public IntelCpu getCpu() {return cpu;}public void setCpu(IntelCpu cpu) {this.cpu = cpu;}public KingstinMemory getMemory() {return memory;}public void setMemory(KingstinMemory memory) {this.memory = memory;}public void run(){System.out.println("运行");String data = hardDisk.get();System.out.println("从硬盘上获取的数据是:"+data);cpu.run();memory.save();}
}public class ComputerDemo {public static void main(String[] args) {XiJieHardDisk hardDisk=new XiJieHardDisk();IntelCpu cpu=new IntelCpu();KingstinMemory memory=new KingstinMemory();Computer c=new Computer();c.setCpu(cpu);c.setHardDisk(hardDisk);c.setMemory(memory);c.run();}
}结果
运行
使用希捷硬盘接受数据
从硬盘上获取的数据是:数据
使用Intel处理器
使用金士顿内存条Process finished with exit code 0

上面代码可以看到已经组装了一台电脑,但是似乎组装的电脑的cpu只能是Intel的,内存条只能是金士顿的,硬盘只能是希捷的,这对用户肯定是不友好的,用户有了机箱肯定是想按照自己的喜好,选择自己喜欢的配件。

根据依赖倒转原则进行改进

代码我们只需要修改Computer类,让Computer类依赖抽象(各个配件的接口),而不是依赖于各个组件具体的实现类。

改进后的Java代码是

public interface HardDisk {public void save(String date);public String get();
}
public class XiJieHardDisk implements HardDisk{public void save(String data){System.out.println("使用希捷影片那存储数据为"+data);}public String get(){System.out.println("使用希捷硬盘接受数据");return "数据";}
}
public interface Cpu {public void run();
}
public class IntelCpu implements Cpu{public void run(){System.out.println("使用Intel处理器");}
}public interface Memory {public void save();
}
public class KingstinMemory implements Memory{public void save(){System.out.println("使用金士顿内存条");}
}
public class Computer {private HardDisk hardDisk;private Cpu cpu;private Memory memory;public HardDisk getHardDisk() {return hardDisk;}public void setHardDisk(HardDisk hardDisk) {this.hardDisk = hardDisk;}public Cpu getCpu() {return cpu;}public void setCpu(Cpu cpu) {this.cpu = cpu;}public Memory getMemory() {return memory;}public void setMemory(Memory memory) {this.memory = memory;}public void run(){System.out.println("运行");String data = hardDisk.get();System.out.println("从硬盘上获取的数据是:"+data);cpu.run();memory.save();}
}
public class ComputerDemo {public static void main(String[] args) {HardDisk hardDisk=new XiJieHardDisk();Memory memory=new KingstinMemory();Cpu cpu=new IntelCpu();Computer computer=new Computer();computer.setHardDisk(hardDisk);computer.setCpu(cpu);computer.setMemory(memory);computer.run();}
}
结果
运行
使用希捷硬盘接受数据
从硬盘上获取的数据是:数据
使用Intel处理器
使用金士顿内存条Process finished with exit code 0

3.5接口隔离原则

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

例子:安全门例子

一个安全门具有防火、防水、防盗的功能。可以将防火、防水、防盗功能提取成一个接口,形成一套规范。

从上面的类图中发现这种设计存在问题,安全门具有防盗、防水、防火的功能。吐过还需要创建另一个安全门,而该安全门只具有防盗、防水功能呢?如果实现了SafetyDoor接口就违背了接口隔离原则,所以我们进行改进。

具体实现的Java代码

public interface AntiTheft {void antiTheft();
}public interface FireProof {void fireProof();
}public interface WaterProof {void waterProof();
}public class SafetyDoor implements AntiTheft,FireProof,WaterProof{@Overridepublic void antiTheft() {System.out.println("防盗");}@Overridepublic void fireProof() {System.out.println("防火");}@Overridepublic void waterProof() {System.out.println("防水");}
}public class SafetyDoorDemo {public static void main(String[] args) {SafetyDoor safetyDoor=new SafetyDoor();safetyDoor.antiTheft();safetyDoor.waterProof();safetyDoor.fireProof();}
}结果
防盗
防水
防火Process finished with exit code 0

3.6迪米特法则

迪米特法则又叫最少知识原则。(只和你的直接朋友交谈,不跟”陌生人“说话)

其含义是:如果两个软件实体无必须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

迪米特法则中的”朋友“是指:当前对象本身,当前对象的成员对象,当前对象所创建的对象,当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。

例子:明星与经纪人的关系实例

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

具体的Java代码实现

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 String getName() {return name;}public Fans(String name) {this.name = name;}
}public class Company {private String name;public String getName() {return name;}public Company(String name) {this.name = name;}
}public class Agent {private Star star;private Fans fans;private Company company;public void setStar(Star star) {this.star = star;}public void setFans(Fans fans) {this.fans = fans;}public void setCompany(Company company) {this.company = company;}public void meeting(){System.out.println(star.getName()+"和粉丝"+fans.getName()+"见面");}public void business(){System.out.println(star.getName()+"和公司"+company.getName()+"商务合作");}
}public class AgentDemo {public static void main(String[] args) {Star star=new Star("刘德华");Fans fans=new Fans("张三");Company company=new Company("阿里巴巴");Agent agent=new Agent();agent.setStar(star);agent.setFans(fans);agent.setCompany(company);agent.meeting();agent.business();}
}
结果
刘德华和粉丝张三见面
刘德华和公司阿里巴巴商务合作Process finished with exit code 0

3.7合成复用原则

合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

通常类的复用分为继承复用和合成复用两种。

继承复用虽然由简单和易实现的优点,但它也存在一些缺点。

继承复用的缺点

1、继承复用破化了类的封装性,因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为”白箱“复用。

2、子类与父类的耦合度高,父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展和维护。

3、它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它具有的优点

1、它维持了类的封装性,因为成分对象的内部细节是新对象看不见的,所以这种复用又称为”黑箱“复用。

2、对象的间的耦合度低,可以在类的成员位置声明抽象。

3、服用的灵活性高,这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

例子:汽车分类管理系统

汽车按”动力源“可划分为汽油汽车、电动汽车;按”颜色“划分为白色汽车、黑色汽车和红色汽车。如果同时考虑这两种分类,其组合就很多,如果我们使用合成复用

Java设计模式第一章(软件设计原则)(学习笔记)相关推荐

  1. 五大软件设计原则学习笔记5——依赖倒置原则

    五大软件设计原则SOLID: 单一职责原则(Single responsibility principle,SRP)开放封闭原则(Open–closed principle,OCP)Liskov 替换 ...

  2. 五大软件设计原则学习笔记4——接口隔离原则

    五大软件设计原则SOLID: 单一职责原则(Single responsibility principle,SRP) 开放封闭原则(Open–closed principle,OCP) Liskov ...

  3. 五大软件设计原则学习笔记3——Liskov 替换原则

    五大软件设计原则SOLID: 单一职责原则(Single responsibility principle,SRP) 开放封闭原则(Open–closed principle,OCP) Liskov ...

  4. 五大软件设计原则学习笔记2——开放封闭原则

    五大软件设计原则SOLID: 单一职责原则(Single responsibility principle,SRP) 开放封闭原则(Open–closed principle,OCP) Liskov ...

  5. 五大软件设计原则学习笔记1——单一职责原则

    五大软件设计原则SOLID: 单一职责原则(Single responsibility principle,SRP) 开放封闭原则(Open–closed principle,OCP) Liskov ...

  6. 引用防删——JAVA设计模式总结之六大设计原则

    JAVA设计模式总结之六大设计原则 从今年的七月份开始学习设计模式到9月底,设计模式全部学完了,在学习期间,总共过了两篇:第一篇看完设计模式后,感觉只是脑子里面有印象但无法言语.于是决定在看一篇,到9 ...

  7. Java 设计模式总结及六大设计原则

    设计模式总结 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式. ...

  8. java设计模式总结之六大设计原则(有图有例子)

    转载:https://www.cnblogs.com/jpfss/p/9765239.html 下面来总结下自己所学习的设计模式,首先我们看下各个模式之间的关系图,下面这张图是网上比较典型的一个类图关 ...

  9. Java设计模式总结——6大设计原则

    从今年的七月份开始学习设计模式到9月底,设计模式全部学完了,在学习期间,总共过了两篇:第一篇看完设计模式后,感觉只是脑子里面有印象但无法言语.于是决定在看一篇,到9月份第二篇设计模式总于看完了,这一篇 ...

最新文章

  1. C语言基础知识【数据类型】
  2. 【深入Java虚拟机JVM 06】垃圾收集概述
  3. python getattr_Python中的getattr()函数详解
  4. python 视频转场_视频剪辑什么鬼?Python 带你高效创作短视频
  5. chrome浏览器无法上网_浏览器无法上网
  6. python基础知识-Python学习--最完整的基础知识大全
  7. 【VMCloud云平台】SCAP(二)
  8. 零基础学启发式算法(4)-模拟退火 (Simulated Annealing)
  9. Linux系统下查看网卡的UUID
  10. 计算机基础第五章知识,计算机基础第5次作业-第五章-Powerpoint知识题
  11. java图书管理系统(MySQL)
  12. ectouch2.0 php5.5_Ectouch2.0 分析解读代码审计流程
  13. Form表单的重置按钮(type=“reset“)无法重置隐藏Input输入框的值
  14. 关于AI的目标导向型行动计划
  15. 腾讯android 热更新,Android 腾讯 Bugly 热更新
  16. XTDorne平台搭建无人机仿真环境时遇到的问题及解决
  17. 小游戏——打鱼还是晒网
  18. python16进制表示0xad_在 Python 中 0xad 是合法的十六进制数字表示形式。 (2.0分)_学小易找答案...
  19. Linux服务--DHCP中继
  20. sklearn 学习之 model_selection

热门文章

  1. 天平游码读数例题_天平游码读数
  2. 降噪耳机推荐,四款优秀的降噪耳机分享
  3. warnings 忽视警告
  4. entity、bo、vo、po 如何理解和区分?
  5. Unity 像机抖动效果
  6. 《转》openstack中删除虚拟主机,状态一直deleting
  7. 从万物归零到虚拟与现实交错
  8. 微信小程序 使用webview 缓存解决办法
  9. Geek 设计师们疯狂的桌面
  10. 【100个 Unity小知识点】☀️ | Unity 中怎样读取Excel文件