精华内容
下载资源
问答
  • 【Java设计模式】软件设计七大原则

    万次阅读 多人点赞 2019-08-31 13:44:31
    文章目录软件设计原则的分类开闭原则依赖倒置原则单一职责原则接口隔离原则迪米特法则(最少知道原则)里氏替换原则合成/复用原则(组合/复用原则) 软件设计原则的分类 开闭原则 依赖倒置原则 单一职责原则 接口...

    软件设计原则的分类

    1. 开闭原则
    2. 依赖倒置原则
    3. 单一职责原则
    4. 接口隔离原则
    5. 迪米特法则(最少知道原则)
    6. 里氏替换原则
    7. 合成/复用原则(组合/复用原则)

    在设计模式中会有这7中软件设计原则的体现,但是值得注意的是这7钟设计原则在设计模式中的使用是有取舍的,有的可能是完整的体现,也可能是部分地体现设计原则;有的可能使用了,有的可能没有使用等。
    软件设计原则的学习应为软件设计模式的学习打好基础

    开闭原则

    定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

    核心思想:用抽象构建框架,用实现扩展细节

    优点:提高软件系统的可复用性及可维护性

    开闭原则是面向对象最基础的设计原则,它能够帮助我们开发稳定灵活的系统

    下面来演示一下:
    场景描述:现在要卖“雪花酥”小蛋糕大礼包,雪花酥小蛋糕大礼包的属性有编号,价格,口味。

    首先定义一个接口:

    package softwaredesign;
    
    public interface ICake {
        Integer getID();
        String getTaste();
        Double getPrice();
    }
    

    实现类

    package softwaredesign;
    
    public class SnowCake implements ICake {
        private Integer id;
        private String taste;
        private Double price;
    
        public SnowCake(Integer id, String taste, Double price) {
            this.id = id;
            this.taste = taste;
            this.price = price;
        }
    
        @Override
        public Integer getID() {
            return this.id;
        }
    
        @Override
        public String getTaste() {
            return this.taste;
        }
    
        @Override
        public Double getPrice() {
            return this.price;
        }
    
        @Override
        public String toString() {
            return "SnowCake{" +
                    "id=" + id +
                    ", taste='" + taste + '\'' +
                    ", price=" + price +
                    '}';
        }
    }
    

    测试:

    package softwaredesign;
    
    public class Test {
        public static void main(String[] args) {
            SnowCake sc = new SnowCake(100,"原味",55.0);
            System.out.println(sc);
        }
    }
    

    结果是正常的
    转换成UML图:
    在这里插入图片描述
    在这里插入图片描述
    从类结构图中 很容易类结构的信息

    最近618 淘宝也搞活动了雪花酥要进行打8折了。

    这个时候的开发要注意了 不要动接口 不要动接口 接口作为契约应该是可靠的,稳定的。

    添加一个类

    package softwaredesign;
    
    public class SnowCakeDiscount extends SnowCake {
        public SnowCakeDiscount(Integer id, String taste, Double price) {
            super(id, taste, price);
        }
    
        @Override
        public Integer getID() {
            return super.getID();
        }
    
        @Override
        public String getTaste() {
            return super.getTaste();
        }
    
        @Override
        public Double getPrice() {
            return super.getPrice()*0.8;
        }
    
        @Override
        public String toString() {
            return "SnowCake{" +
                    "id=" + super.getID() +
                    ", taste='" + super.getTaste()+ '\'' +
                    ", price=" + super.getPrice()*0.8 +
                    '}';
        }
    }
    

    测试

    package softwaredesign;
    
    public class Test {
        public static void main(String[] args) {
            SnowCake sc = new SnowCakeDiscount(100,"原味",55.0);
            System.out.println(sc);
        }
    }
    

    在这里插入图片描述
    在这里插入图片描述
    上述是UML图

    依赖倒置原则

    定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象

    抽象不应该依赖细节;细节应该依赖抽象

    针对接口编程,不要针对实现编程

    注意
    每个类都尽量继承自接口或者抽象类(可以继承抽象类实现接口方法)
    尽量不要从具体的类派生
    尽量不要覆盖其基类的方法

    优点:
    可以减少类间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险

    下面来演示一下:
    小明对淘宝上卖的鼠标 和 键盘都比较感兴趣

    首先演示一个面向实现编程的例子
    Buy类

    package softwaredesign;
    
    public class Buy {
        public void buyMouse(){
            System.out.println("小明在买鼠标");
        }
        public void buyKeyboard(){
            System.out.println("小明在买键盘");
        }
    }
    

    Test

    package softwaredesign;
    
    public class Test {
        public static void main(String[] args) {
           Buy buy = new Buy();
           buy.buyMouse();
           buy.buyKeyboard();
        }
    }
    

    假如小明又想买 鼠标垫
    在Buy类中添加

    public void buyMousePad(){
    	System.out.println("小明在买键盘垫");
    }
    

    就是妥妥的面向实现编程
    这个是与 依赖倒置 原则 是背道而驰的

    高层次的模块不应该依赖低层次的模块

    下面演示 满足依赖倒置原则的情况
    创建一个Things接口

    package softwaredesign;
    
    public interface Things {
        void buyThins();
    }
    
    

    继承接口的类:

    package softwaredesign;
    
    public class BuyMouse implements Things {
        @Override
        public void buyThins() {
            System.out.println("小明正在买鼠标");
        }
    }
    
    package softwaredesign;
    
    public class BuyKeyboard implements Things {
        @Override
        public void buyThins() {
            System.out.println("小明正在买键盘");
        }
    }
    

    将继承接口的类联系起来

    package softwaredesign;
    
    public class Buy {
        public void buy(Things things){
            things.buyThins();
        }
    }
    

    测试

    package softwaredesign;
    
    public class Test {
        public static void main(String[] args) {
           Buy b = new Buy();
           b.buy(new BuyMouse());
           b.buy(new BuyKeyboard());
        }
    }
    

    打印结果与前面面向实现的结果是一样的

    我们看一下类图:
    在这里插入图片描述
    如果有拓展的要买的东西 都和BuyMouse和BuyKeyboard平级了

    具体的实现类 Buy类是不需要动的

    也就是说我们面向接口编程, 不需要变动实现类

    这里的Test测试类 和 Buy类是解耦的


    当然也可以通过构造器注入 来进行Buy的实现
    没有改动的

    package softwaredesign;
    
    public class Buy {
        public void buy(Things things){
            things.buyThins();
        }
    }
    

    改动后

    package softwaredesign;
    
    public class Buy {
        private Things things;
    
        public Buy(Things things){
            this.things = things;
        }
    
        public void buy(){
            things.buyThins();
        }
    }
    
    

    具体的测试

    package softwaredesign;
    
    public class Test {
        public static void main(String[] args) {
           Buy b = new Buy(new BuyMouse());
           b.buy();
        }
    }
    

    构造器使用方式并不是非常方便 需要拓展的时候 还有new一个出来Buy实现类
    在Spring框架中默认的设计模式是 单例模式


    之前是构造器方式
    现在取消构造器方式 改用Setter方法
    修改Buy类

    package softwaredesign;
    
    public class Buy {
    
        private Things things;
    
        public void setThings(Things things) {
            this.things = things;
        }
    
        public void buy(){
            things.buyThins();
        }
    }
    
    

    测试

    package softwaredesign;
    
    public class Test {
        public static void main(String[] args) {
           Buy b = new Buy();
           b.setThings(new BuyMouse());
           b.buy();
        }
    }
    
    

    看看现在的类图
    在这里插入图片描述
    Buy类不依赖与任何继承Things的类

    单一职责原则

    定义:不要存在多于一个导致类变更的原因

    一个类/接口/方法只负责一项职责

    优点:降低类的复杂度、提高类的可读性,提高系统的可维护性、降低变更引起的风险

    下面进行演示:
    创建一个鸟类 违反单一原则

    package softwareDesign;
    
    public class Bird {
        public void mainMoveMode(String birdName){
            if ("变色龙".equals(birdName)){
                System.out.println("变色龙用四肢走");
            }else {
                System.out.println(birdName+"用翅膀飞");
            }
        }
    }
    
    

    测试

    package softwareDesign;
    
    public class Test {
        public static void main(String[] args) {
            Bird bird = new Bird();
            bird.mainMoveMode("猫头鹰");
            bird.mainMoveMode("变色龙");
        }
    }
    

    假如又要添加新的东西 又要去动Bird类了(因为Bird类负责的职责较多),随着代码的复杂,越动出错的风险越大

    改变:

    package softwareDesign;
    
    public class FlyBird {
        public void mainMoveMode(String birdName) {
            System.out.println(birdName + "用翅膀飞");
        }
    }
    
    package softwareDesign;
    
    public class WalkAnimal {
        public void mainMoveMode(String Name) {
            System.out.println(Name + "用四肢走");
        }
    }
    
    package softwareDesign;
    
    public class Test {
        public static void main(String[] args) {
            FlyBird flyBird = new FlyBird();
            WalkAnimal walkAnimal = new WalkAnimal();
            //应用层判断
            flyBird.mainMoveMode("猫头鹰");
            walkAnimal.mainMoveMode("变色龙");
        }
    }
    

    我们来看一下类图
    在这里插入图片描述
    当然还有接口 以及 方法的情况
    结合上面的示例 这个都很好理解的

    接口隔离原则

    定义:用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口

    一个类对一个类的依赖应该建立在最小的接口上

    建立单一接口,不要建立庞大臃肿的接口尽量细化接口,接口中的方法尽量少

    尽量细化接口,接口中的方法尽量少

    注意适度原则,一定要适度

    优点:符合我们常说的高内聚低耦合的设计思想从而使得类具有很好的可读性、可扩展性和可维护性。

    比如说 一个接口中定义了eat() fly() swim()
    狗继承了这个接口 但是多了一个fly()
    猫头鹰记录了这个接口 但是多一个swim()
    它们都要空实现

    如果要符合接口隔离原则,需要为fly(0 eat()
    和 swim() eat() 或者 为fly() eat() swim()分别定义一个接口

    但是一定要适度适度 差不多细化就行了 否则会导致接口过多 反而增大开发难度

    迪米特法则(最少知道原则)

    定义:一个对象应该对其他对象保持最少的了解。又叫最少知道原则

    尽量降低类与类之间的耦合

    优点:降低类之间的耦合、

    这个也是需要适度的!!!

    强调只和朋友交流,不和陌生人说话

    朋友:
    出现在成员变量、方法的输入、输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。

    演示
    不符合迪米特法则的情形:

    package softwareDesign;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Boss {
        public void commandCheckNumber(TeamLeader teamLeader){
            List<Project> list = new ArrayList<>();
            for (int i = 0; i <3 ; i++) {
                list.add(new Project());
            }
            teamLeader.checkNumberOfProject(list);
        }
    }
    
    package softwareDesign;
    
    import java.util.List;
    
    public class TeamLeader {
        public void checkNumberOfProject(List<Project> projectList){
            System.out.println("项目数量是: "+projectList.size());
        }
    }
    
    
    package softwareDesign;
    
    public class Project {
    }
    
    

    测试

    package softwareDesign;
    
    public class Test {
        public static void main(String[] args) {
            Boss boss = new Boss();
            TeamLeader teamLeader = new TeamLeader();
            boss.commandCheckNumber(teamLeader);
        }
    }
    
    

    我们看看它的类图
    在这里插入图片描述
    从代码中可以分析出
    Boss类中 可以跟Project类没有关系的 直接调用TeamLeader就行了

    修改:

    package softwareDesign;
    
    public class Boss {
        public void commandCheckNumber(TeamLeader teamLeader){
            teamLeader.checkNumberOfProject();
        }
    }
    
    
    package softwareDesign;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class TeamLeader {
        public void checkNumberOfProject(){
            List<Project> list = new ArrayList<>();
            for (int i = 0; i <3 ; i++) {
                list.add(new Project());
            }
            System.out.println("项目数量是: "+list.size());
        }
    }
    
    
    package softwareDesign;
    
    public class Project {
    }
    
    

    测试

    package softwareDesign;
    
    public class Test {
        public static void main(String[] args) {
            Boss boss = new Boss();
            TeamLeader teamLeader = new TeamLeader();
            boss.commandCheckNumber(teamLeader);
        }
    }
    
    

    只有 Boss和TeamLeader类 做了改动
    下面是类图:
    在这里插入图片描述

    里氏替换原则

    定义:
    如果对每一个类型为T1的对象o1,都有类型为T2的对象02,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。

    定义扩展:
    一个软件实体如果适用一个父类的话,那一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。

    引申意义:子类可以扩展父类的功能,但不能改变父类原有的功能。
    ◆含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
    ◆含义2:子类中可以增加自己特有的方法。

    结合"开闭原则"举的例子 可以很容易看出来 前两条的出入

    ◆含义3:当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。
    ◆含义4:当子类的方法实现父类的方法时(重写/重载或实现抽象方法)方法的后置条件(即方法的输出/返回值)要比父类更严格或相等。

    它的优点:
    ◆优点1:约束继承泛滥,开闭原则的一种体现。
    ◆优点2:加强程序的健壮性,同时变更时也可以做到非常好的兼容性提高程序的维护性、扩展性。降低需求变更时引入的风险。

    演示:
    Rectangle.java

    package softwareDesign;
    
    public class Rectangle {
        private long length;
        private long width;
    
        public long getLength() {
            return length;
        }
    
        public long getWidth() {
            return width;
        }
    
        public void setLength(long length) {
            this.length = length;
        }
    
        public void setWidth(long width) {
            this.width = width;
        }
    }
    
    

    Squre.java

    package softwareDesign;
    
    public class Square extends Rectangle{
        private long sideLength;
    
        public long getSideLength() {
            return sideLength;
        }
    
        public void setSideLength(long sideLength) {
            this.sideLength = sideLength;
        }
    
        @Override
        public long getLength() {
            return getSideLength();
        }
    
        @Override
        public long getWidth() {
            return getLength();
        }
    
        @Override
        public void setLength(long length) {
           setSideLength(length);
        }
    
        @Override
        public void setWidth(long width) {
            setSideLength(width);
        }
    }
    
    

    Test.java

    package softwareDesign;
    
    public class Test {
        public static void resize(Rectangle rectangle) {
            while (rectangle.getWidth()<=rectangle.getLength()){
                rectangle.setWidth(rectangle.getWidth()+1);
                System.out.println("width:"+rectangle.getWidth()+" length:"+rectangle.getLength());
            }
            System.out.println("方法结束:width:"+rectangle.getWidth()+" length:"+rectangle.getLength());
        }
    
        public static void main(String[] args){
            Rectangle rectangle = new Rectangle();
            rectangle.setWidth(10);
            rectangle.setLength(20);
            resize(rectangle);
        }
    }
    

    注意测试的是 长方形的实例
    如果换成了正方形 那么这个程序将会永久的执行下去 直到溢出
    不符合 里氏替换原则

    我们可以改进一下:
    我们可以创建一个基于长方形和正方形的父类:

    package softwareDesign;
    
    public interface QRangle {
        long getWidth();
        long getLength();
    }
    
    
    package softwareDesign;
    
    public class Rectangle implements QRangle{
        private long length;
        private long width;
    
        public long getLength() {
            return length;
        }
    
        public long getWidth() {
            return width;
        }
    
        public void setLength(long length) {
            this.length = length;
        }
    
        public void setWidth(long width) {
            this.width = width;
        }
    }
    
    package softwareDesign;
    
    public class Square implements QRangle{
        private long sideLength;
    
        public long getSideLength() {
            return sideLength;
        }
    
        public void setSideLength(long sideLength) {
            this.sideLength = sideLength;
        }
    
        public long getWidth() {
            return sideLength;
        }
    
        public long getLength() {
            return sideLength;
        }
    }
    
    

    这样子在测试中 使用之前的方式 会报错 因为他不满足里氏替换原则

    合成/复用原则(组合/复用原则)

    定义:尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的

    聚合has-A和组合contains-A

    优点:可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少

    不满足的设计

    package softwareDesign;
    
    public class DBConnection {
        public String getConnection(){
            return "MySQL数据库连接";
        }
    }
    
    
    package softwareDesign;
    
    public class ProductDao extends DBConnection {
        public void addProduct(){
            String conn = super.getConnection();
            System.out.println("使用"+conn+"增加产品");
        }
    }
    
    

    Test

    package softwareDesign;
    
    public class Test {
    
        public static void main(String[] args){
            ProductDao productDao = new ProductDao();
            productDao.addProduct();
        }
    }
    

    UML类图
    在这里插入图片描述
    如果又接入了别的数据库
    我们能够直接修改DBConnection类
    但是这样违反开闭原则

    我们重构一下它 让它在具备扩展能力且不违反开闭原则,还遵守合成/复用原则
    抽象类

    package softwareDesign;
    
    public abstract class DBConnection {
        public abstract String getConnection();
    }
    
    
    package softwareDesign;
    
    public class MySQLConnection extends DBConnection{
        @Override
        public String getConnection() {
            return "MySQL数据库连接";
        }
    }
    
    
    package softwareDesign;
    
    public class PostgreSQLConnection extends DBConnection {
        @Override
        public String getConnection() {
            return " PostgreSQL数据库连接";
        }
    }
    
    
    package softwareDesign;
    
    public class ProductDao  {
        private DBConnection dbConnection;
    
        public void setDbConnection(DBConnection dbConnection) {
            this.dbConnection = dbConnection;
        }
    
        public void addProduct(){
            //通过组合的方式
            String conn =dbConnection.getConnection();
            System.out.println("使用"+conn+"增加产品");
        }
    }
    
    

    测试

    package softwareDesign;
    
    public class Test {
    
        public static void main(String[] args){
            ProductDao productDao = new ProductDao();
            productDao.setDbConnection(new MySQLConnection());
            productDao.addProduct();
        }
    }
    
    

    查看UML图
    在这里插入图片描述
    凡是拓展的都是第二层的平级

    展开全文
  • 软件设计原则

    2014-04-20 21:49:19
    所以,可以说软件系统是连接需求分析、硬件系统以及使得系统实现的桥梁,对软件的设计应首先了解软件设计设计原则:  (1)可靠性  用软件系统规模越做越大越复杂,其可靠性越来越难保证。应用本身
    系统
    软件
    是控制和协调
    计算机
    以及
    外部设备
    ,支持应用的软件开发和运行的系统,是不需要用户干预的各种
    程序
    的集合。主要功能是调度、监控和维护
    计算机系统
    ,负责管理计算机系统中各种独立的硬件,使得它们可以协调工作。所以,可以说
    软件系统
    是连接
    需求分析
    
    硬件系统
    以及使得系统实现的桥梁,对软件的设计应首先了解软件设计的设计原则:
    
      (1)可靠性
      用软件系统规模越做越大越复杂,其可靠性越来越难保证。应用本身对系统运行的可靠性要求越来越高,软件系统的可靠性也直接关系到设计自身的声誉和生存发展竞争能力。 软件可靠性 意味着该软件在测试运行过程中避免可能发生故障的能力,且一旦发生故障后,具有解脱和排除故障的能力。软件可靠性和硬件可靠性本质区别在于:后者为物理机理的衰变和老化所致,而前者是由于设计和实现的错误所致。故软件的可靠性必须在 设计阶段 就确定,在生产和测试阶段再考虑就困难了。
      (2) 健壮性
      健壮性又称 鲁棒性 ,是指软件对于规范要求以外的输入能够判断出这个输入不符合规范要求,并能有合理的处理方式。软件健壮性是一个比较模糊的概念,但是却是非常重要的软件外部量度标准。软件设计的健壮与否直接反应了分析设计和编码人员的水平。
      (3)可修改性
      要求以科学的方法设计软件,使之有良好的结构和完备的文档,系统性能易于调整。
      (4)容易理解
      软件的可理解性是其可靠性和可修改性的前提。它并不仅仅是文档清晰可读的问题,更要求软件本身具有简单明了的结构。这在很大程度上取决于设计者的洞察力和创造性,以及对设计对象掌握得透彻程度,当然它还依赖于设计工具和方法的适当运用。
      (5)程序简便
      (6) 可测试性
      可测试性就是设计一个适当的数据集合,用来测试所建立的系统,并保证系统得到全面的检验。
      (7) 效率性
      软件的效率性一般用程序的执行时间和所占用的 内存容量 来度量。在达到原理要求功能指标的前提下,程序运行所需时间愈短和占用 存储容量 愈小,则效率愈高。
      (8) 标准化原则
      在结构上实现开放,基于业界开放式标准,符合国家和信息产业部的规范。
      (9)先进性
      满足 客户需求 ,系统性能可靠,易于维护。
      (10)可扩展性
      软件设计完要留有升级接口和升级空间。
    展开全文
  • 软件设计的七大设计原则

    千次阅读 2019-01-05 00:02:35
    七大设计原则是23种设计模式的基础,体现了软件设计的思想,但并不是所有设计模式都遵循这七大设计原则,有些设计模式只遵循一部分设计原则,是对一些实际情况做的一些取舍。在我们项目中也并一定完全遵循所有设计...

    一、前言

          七大设计原则是23种设计模式的基础,体现了软件设计的思想,但并不是所有设计模式都遵循这七大设计原则,有些设计模式只遵循一部分设计原则,是对一些实际情况做的一些取舍。在我们项目中也并不一定完全遵循所有设计模式,因为受一些因素如时间、人力、成本等,如果一开始将扩展性做的很完美,那么成本就上来了。所以遵循设计模式不要过度,一定要适度。
          本文将讲解每一种设计原则,从定义开始进行分析,理解,然后使用代码示例和UML图,分析存在的问题,逐步演进,力求将设计原则讲透,使读者理解。后续的设计模式文章也将采用这种方式。

    二、开闭原则

          开闭原则的定义是:一个软件实体(如类、模块、函数)应该对扩展开放、对修改关闭。生活中也有很多开闭原则的体现,比如公司的8小时弹性上班制度,对8小时的上班时间是的修改是关闭的,但是什么时候来什么时候走是开放的。
          开闭原则的含义其实是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现。在项目中需求变更是非常常见的,如果我们频繁修改原有的代码会增加系统的复杂度,增加项目的风险。使用开闭原则可以提高系统的可复用性及可维护性,具体做法就是用抽象构建框架,用实现扩展细节。
          下面用一个例子来讲解开闭原则,有一个在线书店卖书的场景,每本书有价格和名称,我们新建一个书的接口IBook,和该接口的一个实现类JavaBook。

    public interface IBook {
        String getName();
        double getPrice();
    }
    public class JavaBook implements IBook {
        @Override
        public String getName() {
            return "Java入门到精通";
        }
        @Override
        public double getPrice() {
            return 68.99;
        }
    }
    

          此时的UML图是这样的(图有点错误,IDEA生成UML时get开头的方法它会认为是属性)
    在这里插入图片描述
          现在有一个需求,就是有些书需要打折销售,我们需要获取打折后的价格,一种思路是修改IBook,新增一个打折价格的方法,但是这种改动影响很大,实现了该接口的类都要实现这个方法;另一种思路就是将JavaBook中的getPrice() 方法修改成打折后的价格,但是这样就获取不到原价了;还一种思路是在JavaBook中新增一个获取打折后价格的方法,这样既能获取原价又能获取打折后的价格,但是这三种种做法都违背了开闭原则,也就是对JavaBook进行了修改来实现变化,正确的做法是新增一个JavaBook的打折类,继承自JavaBook,新增一个获取打折后价格的方法。

    public class JavaDiscountBook extends JavaBook {
        double getDiscountPrice(){
            return super.getPrice() * 0.8;
        }
    }
    

          此时的UML图是:
    在这里插入图片描述
          这样在不修改原来代码的基础上,实现了需求变更,其实实现开闭原则的核心是面向抽象编程,后面一些设计原则也是如此。

    三、依赖倒置原则

          依赖倒置原则定义:高层模块不应该依赖底层模块,二者都应该依赖其抽象。也就是说针对接口编程,不要针对实现编程,针对接口编程包括使用接口或抽象类,这样可以使得各个模块彼此独立,降低模块间的耦合性。而且在实现类中尽量不发生直接的依赖关系,依赖关系通过接口或抽象类产生。
          有一个场景,司机可以开车,我们新建一个Driver类和一个Benz类,司机可以开奔驰车,代码如下:

    public class Driver {
        public void driver(Benz benz){
            benz.run();
        }
    }
    public class Benz {
        public void run(){
            System.out.println("奔驰车可以跑!");
        }
    }
    

    此时的UML图是这样的
    在这里插入图片描述
          Driver类就依赖Benz类,显然违反了依赖倒置原则,如果我们司机想开其他车,就必须修改Driver类,我们进一步修改,增加IDriver的接口和ICar接口,IDriver的driver类参数是ICar,这样使得依赖关系发生在这两个接口上,不同的司机实现IDriver接口,不同的车实现ICar接口就可以了,代码如下:

    public interface IDriver {
        void driver(ICar car);
    }
    public interface ICar {
        void run();
    }
    public class Benz implements ICar{
        @Override
        public void run(){
            System.out.println("奔驰车可以跑!");
        }
    }
    public class Driver implements IDriver{
        @Override
        public void driver(ICar car){
            car.run();
        }
    }
    

          此时的UML图是这样的
    在这里插入图片描述
          这样不论什么类型的车都可以传入Driver的driver()方法里面,进行调用。

    四、 单一职责原则

          单一职责原则的定义:不要存在多于一个导致类变更的原因。如果我们一个类有两个职责:职责1和职责2,当我们需求变更的时候,职责1需要改变,变更的时候很可能会导致原本正常的职责2出问题。所以一个类、接口方法只负责一项职责,这样能降低类的复杂度,提高类的可读性,提高可维护性,降低修改带来的风险。在实际项目中,很多类不遵循单一职责原则,但是接口和方法要做到单一职责。单一职责原则还有一个很重要的点就是职责的划分,有些需求正常情况下有多个职责,但是某些特殊情况下又是一个职责,职责划分也需要视实际情况而定。

    五、接口隔离原则

          接口隔离原则定义:用多个专门的接口而不使用单一的总接口,客户端不应该依赖它不需要的接口。也就是说一个类对另一个类的依赖应该建立在最小的接口上,尽量细化接口,减少接口中的方法,但是一定要注意适度的原则,过分细化接口会带来复杂度。
          比如我们有个接口IAnimalAction,描述动物的行为,代码如下:

    public interface IAnimalAction {
        void eat();
        void fly();
        void swim();
    }
    

          这个接口中,我们定义了三个行为,后面需要的动物类实现这个接口,但是这就存在一个问题,如果某些动物不具备接口里面的三个行为中的某一个,但是它必须要实现那个方法,这就违背了接口隔离原则,正确的做法是将接口中的三个方法隔离开,分成三个接口,这样有具体行为的动物实现具体的接口,减少了耦合,将代码演进:

    public interface IEatAnimal {
        void eat();
    }
    public interface IFlyAnimal {
        void fly();
    }
    public interface ISwimAnimal {
        void swim();
    }
    

          接口隔离原则强调的是接口依赖隔离,单一职责原则强调的是职责单一。单一职责是对实现的约束,接口隔离原则是对抽象的约束。

    六、迪米特原则

          迪米特原则又叫最少知道原则,定义是:一个对象应该对其他对象保持最少的了解。简单讲就是只和朋友交流,不和陌生人说话,朋友指的是出现在成员变量、方法输入、方法输出中的类,但是出现在方法内部的类不属于朋友。不应该和这样的类发生关系。使用迪米特原则可以降低类与类之间的耦合,提高类的复用率,但是还是要强调适度的原则,过分使用迪米特原则会产生大量的中介类,使系统变复杂。
          现在有一个场景,学校里面有多个班级,班级里面有多个学生,我们现在要打印所有班级的所有学生,一种实现方式如下(这里将成员变量设置成public省略了get、set方法):

    public class School {
        public int id;
        public String schoolName;
        public List<Class> classes;
        public void print(){
            for(Class c : classes){
                for(Student s : c.students){
                    System.out.println(s.studentName);
                }
            }
        }
    }
    class Class{
        public int id;
        public String className;
        public List<Student> students;
    }
    class Student{
        public int id;
        public String studentName;
    }
    

          在School类中,Class类是它的属性,也就是它的朋友,但是Student类既不是成员变量,方法入参,也不是方法返回值,它不是School类的朋友,不应爱出现在方法内部,应该让Class类打印本班级的学生,代码演进如下:

    public class School {
        public int id;
        public String schoolName;
        public List<Class> classes;
        public void print(){
            for(Class c : classes){
                c.print();
            }
        }
    }
    class Class{
        public int id;
        public String className;
        public List<Student> students;
        public void print(){
            for(Student s : students){
                System.out.println(s.studentName);
            }
        }
    }
    class Student{
        public int id;
        public String studentName;
    }
    

          这样School类和Student就没有耦合了,逻辑也很清晰,符合迪米特原则。

    七、里氏替换原则

          里氏替换原则的定义是:对于每一个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有对象O1替换为O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。也就是子类替换父类,程序逻辑不变。里氏替换原则约束了继承,继承在程序设计中能够复用代码但是对程序是有入侵的,因为子类默认就拥有父类的行为,而且增加了耦合。
          在继承中如何遵守里氏替换原则?首先子类可以扩展父类的功能,但不能改变原有的功能,子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法,如果重写了,那么用子类替换父类时,程序会调用子类的方法(否则重写就没有意义了),也就导致程序的行为发生变化。还有就是子类重载(不是重写)父类方法时,方法的前置条件(方法入参)要比父类更宽松比如父类的参数是HashMap,子类的话参数可以是Map,更宽松的话替换或不替换程序都会调用父类的方法,程序的行为也就不会改变,符合里氏替换原则。同理,方法的后置(方法返回值)条件要比父类更严格。使用里氏替换原则,可以避免子类重写父类的方法,降低代码出错的可能性。

    八、合成复用原则

          合成复用原则的定义是:尽量使用对象的组合/聚合,而不是继承关系达到软件复用的目的。
          组合是contains-A的关系,比如一个人的手、脚就是组合关系,这是一种强关系,其中一部分不存在了,所有的都不存在了,聚合是has-A的关系,是一种弱的关系,人群中的人就是聚合关系,其中一个人离开了,人群还是存在的,通过组合聚合也可以达到复用的目的,但是这种复用是黑箱复用,不需要知道细节,继承的复用是白箱复用,父类的细节会暴露给子类。尽量使用组合/聚合来实现软件复用并不是说抛弃继承,如果两个实体是is-A的关系时,可以使用继承。
          现在有个场景就是数据访问层要操作数据,需要先获得数据库连接,我们新建两个类:

    public class DBConnection {
        public String getConnection() {
            return "获得数据库连接";
        }
    }
    public class ProductDao extends DBConnection {
        private void addProduct(){
            getConnection();
            System.out.println("操作数据库!");
        }
    }
    

          通过继承我们实现了获取数据库连接,但是如果我们要更换一种数据库,那么就要修改DBConnection类,违背了开闭原则,现在我们可以通过抽象加组合的方式来实现变更后的需求,代码演进如下:

    public abstract class DBConnection {
        public abstract String getConnection();
    }
    public class MySQLConnection extends DBConnection {
        @Override
        public String getConnection() {
            return "MySQL的数据库连接";
        }
    }
    public class OracleConnection extends DBConnection {
        @Override
        public String getConnection() {
            return "Oracle数据库连接";
        }
    }
    public class ProductDao{
    
        private DBConnection dbConnection;
    
        ProductDao(DBConnection dbConnection){
            this.dbConnection = dbConnection;
        }
        public void addProduct(){
            String con = dbConnection.getConnection();
            System.out.println("使用"+con + "增加一个产品");
        }
    }
    

          这样,当我们新增一种数据库连接的时候,只要继承DBConnection这个抽象类就可以了,按我们实际传入的类型,ProductDao会调用对应的数据库连接来操作数据库,此时的UML是这样的。
    在这里插入图片描述

    展开全文
  • 在本讲中,我来为大家介绍一下软件设计原则里面的第2个原则,即里氏代换原则。 概述 首先,大家应该知道,里氏代换原则是面向对象设计的基本原则之一。那什么是里氏代换原则呢?里氏代换原则是指任何基类可以出现的...

    在本讲中,我来为大家介绍一下软件设计原则里面的第二个原则,即里氏代换原则。

    概述

    首先,大家应该知道,里氏代换原则是面向对象设计的基本原则之一。那什么是里氏代换原则呢?里氏代换原则是指任何基类可以出现的地方,子类一定可以出现。这句话不好理解,但大家可以通俗理解成子类可以扩展父类的功能,但不能改变父类原有的功能。现在,这句话就好理解很多了,指的就是在Java里面通常都会有父子类的关系,一般而言,我们都会将子类中的功能抽取到父类中,以提高代码的复用性,而在子类中,我们只需要去定义子类特有的功能即可。

    换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。为什么呢?因为如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性就会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。

    你想啊,要是在父类中已经声明了一个方法,而你又在子类中再进行了一个重写,那么在父类中定义的方法是不是就没有任何意义了?如果说父类定义规则,要求子类必须重写,那么在父类中只需要定义成抽象的方法就可以了。

    经过我上面的描述,相信大家对里氏代换原则有了一个简单的认识。接下来,我就为大家介绍里氏替换原则中的一个经典的案例,即正方形不是长方形。

    案例

    案例分析

    在数学领域里,正方形毫无疑问是长方形,它是一个长宽相等的长方形。所以,如果我们要开发一个与几何图形相关的软件系统的话,那么就可以顺理成章的让正方形继承自长方形了。

    请看一下下面这张类图。

    在这里插入图片描述

    可以看到,这张类图里面有三个类,第一个是长方形类,长方形类里面有两个成员变量,一个是length,表示长,一个是width,表示宽,而且它里面还提供了相应的getter和setter方法,相对来说,这个类还是很简单的,比较好理解。

    第二个是长方形类的子类,即正方形类,该类要重写父类中设置长和宽的这两个方法。为什么要重写呢?因为正方形里面的长和宽是相等的。

    以上两个类介绍完之后,再来看最后一个类,即测试类,在测试类中应该提供这么几个方法:

    • 主方法:这里面我没有写出来
    • resize方法:扩宽方法。长方形里面的宽是要比长小的,如果宽比长小的话,那么我们就可以通过该方法来进行判断,然后再将宽给它扩长,直到比长大就OK了
    • 打印长和宽的方法:该方法只是为了更好的看到效果而已

    注意了,在resize方法和打印长和宽的这两个方法里面,还需要传递一个长方形类型的参数,也就是说测试类其实是依赖于长方形类的,所以它俩之间是一个依赖关系。

    把以上这三个类以及依赖关系理清楚了之后,接下来我们就要编写代码来实现这个案例了。

    案例实现

    打开咱们的maven工程,然后在com.meimeixia.principles包下创建一个子包,即demo2,接着在com.meimeixia.principles.demo2包下再创建一个子包,即before,我们首次是在该包下来存放咱们编写的代码的。接下来,我们就要正式开始编写代码来实现以上案例了。

    首先,在com.meimeixia.principles.demo2.before包下新建第一个类,即长方形类,名字可取做Rectangle。

    package com.meimeixia.principles.demo2.before;
    
    /**
     * 长方形类
     * @author liayun
     * @create 2021-05-27 13:26
     */
    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;
        }
    }
    

    然后,新建第二个类,即正方形类,名字可取做Square,记住要让该类去继承长方形类,并重写父类中设置长和宽的方法。那么应该如何去重写呢?很简单,就拿重写父类中设置长的setLength方法来说,我们只需要调用父类中的设置长和宽的方法把方法中的length参数设置给长和宽即可,因为长和宽必须保持一致。当然,重写父类中设置长的setWidth方法也是同理。

    package com.meimeixia.principles.demo2.before;
    
    /**
     * 正方形类
     * @author liayun
     * @create 2021-05-27 13:32
     */
    public class Square extends Rectangle {
    
        @Override
        public void setLength(double length) {
            super.setLength(length);
            super.setWidth(length);
        }
    
        @Override
        public void setWidth(double width) {
            super.setLength(width);
            super.setWidth(width);
        }
    
    }
    

    接着,我们就要编写测试类了,名字就叫RectangleDemo。根据我们上面的分析,相信你一定能写出下面的代码,只不过现在还未在主方法中编写测试代码。

    package com.meimeixia.principles.demo2.before;
    
    /**
     * @author liayun
     * @create 2021-05-27 13:42
     */
    public class RectangleDemo {
    
        public static void main(String[] args) {
            // 测试代码...
        }
    
        // 扩宽方法
        public static void resize(Rectangle rectangle) {
            // 判断宽如果比长小,那么则进行扩宽的操作
            while (rectangle.getWidth() <= rectangle.getLength()) {
                rectangle.setWidth(rectangle.getWidth() + 1);
            }
        }
    
        // 打印长和宽
        public static void printLengthAndWidth(Rectangle rectangle) {
            System.out.println(rectangle.getLength());
            System.out.println(rectangle.getWidth());
        }
    
    }
    

    紧接着,在主方法中编写如下代码进行测试。

    package com.meimeixia.principles.demo2.before;
    
    /**
     * @author liayun
     * @create 2021-05-27 13:42
     */
    public class RectangleDemo {
    
        public static void main(String[] args) {
            // 创建长方形对象
            Rectangle r = new Rectangle();
            // 设置长和宽
            r.setLength(20);
            r.setWidth(10);
            // 调用resize方法进行扩宽
            resize(r);
            printLengthAndWidth(r);
        }
    
        // 扩宽方法
        public static void resize(Rectangle rectangle) {
            // 判断宽如果比长小,那么则进行扩宽的操作
            while (rectangle.getWidth() <= rectangle.getLength()) {
                rectangle.setWidth(rectangle.getWidth() + 1);
            }
        }
    
        // 打印长和宽
        public static void printLengthAndWidth(Rectangle rectangle) {
            System.out.println(rectangle.getLength());
            System.out.println(rectangle.getWidth());
        }
    
    }
    

    这时,我们不妨来运行一下以上测试类,看看打印结果是啥?如下图所示,可以看到长是20,没变,宽是21,因为我们进行了一个扩宽的操作,此时,宽已经比长大了。

    在这里插入图片描述

    如果我像下面这样向resize方法中传入一个正方形类型的对象,那么可不可以呢?

    package com.meimeixia.principles.demo2.before;
    
    /**
     * @author liayun
     * @create 2021-05-27 13:42
     */
    public class RectangleDemo {
    
        public static void main(String[] args) {
            // 创建长方形对象
            Rectangle r = new Rectangle();
            // 设置长和宽
            r.setLength(20);
            r.setWidth(10);
            // 调用resize方法进行扩宽
            resize(r);
            printLengthAndWidth(r);
    
            System.out.println("=============================");
            // 创建正方形对象
            Square s = new Square();
            // 设置长和宽
            s.setLength(10);
            // 调用resize方法进行扩宽
            resize(s);
            printLengthAndWidth(s);
        }
    
        // 扩宽方法
        public static void resize(Rectangle rectangle) {
            // 判断宽如果比长小,那么则进行扩宽的操作
            while (rectangle.getWidth() <= rectangle.getLength()) {
                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),并在这个接口里面定义两个抽象的方法,一个是getLength,一个是getWidth,分别用于获取长和宽,然后让Rectangle类和Square类实现Quadrilateral接口;从以上类图中可以看到,我们还在Square类里面定义了一个名字为side的成员变量,也即正方形的边长,而且在该类里面,除了提供该成员变量的getter和setter方法之外,我们还重写了Quadrilateral接口里面的抽象方法;至于Rectangle类,依旧还是原先的设计,该类是没有任何变化的。

    最后,大家不要忘了,还有一个测试类(即RectangleDemo),该测试类是没有成员变量的,只有如下三个方法:

    • 主方法:这里面我没有声明出来,主要作测试用
    • resize方法:扩宽方法。注意,该方法需要的是一个长方形类型的对象,正方形类型的对象此时是不能传入进来的
    • printLengthAndWidth方法:打印长和宽的方法。注意,该方法需要传递的是一个Quadrilateral接口的子实现类对象

    这样一路分析下来,你就会发现该测试类不仅得依赖Quadrilateral接口,还得依赖Rectangle类。至此,我就给大家分析完以上类图了,接下来,我们就得编写代码来实现以上改进后的案例了。

    首先,在com.meimeixia.principles.demo2包下再创建一个子包,即after,该包下存放的就是改进后的案例的代码。

    然后,我们再创建一个四边形接口。

    package com.meimeixia.principles.demo2.after;
    
    /**
     * 四边形接口
     * @author liayun
     * @create 2021-05-27 14:27
     */
    public interface Quadrilateral {
    
        // 获取长
        double getLength();
    
        // 获取宽
        double getWidth();
    
    }
    

    接着,再来创建咱们的正方形类,注意了,该类是要去实现四边形接口的,这样,我们还必须得重写其中的方法。此外,在该类里面我们还得声明一个表示边长的成员变量,当然还得提供其对应的getter和setter方法。

    package com.meimeixia.principles.demo2.after;
    
    /**
     * 正方形类
     * @author liayun
     * @create 2021-05-27 14:32
     */
    public class Square implements Quadrilateral {
        private double side;
    
        public double getSide() {
            return side;
        }
    
        public void setSide(double side) {
            this.side = side;
        }
    
        @Override
        public double getLength() {
            return side;
        }
    
        @Override
        public double getWidth() {
            return side;
        }
    }
    

    紧接着,再来创建咱们的长方形类,同理,该类也得实现四边形接口,重写其里面的抽象方法。

    package com.meimeixia.principles.demo2.after;
    
    /**
     * 长方形类
     * @author liayun
     * @create 2021-05-27 14:37
     */
    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;
        }
    
        @Override
        public double getLength() {
            return length;
        }
    
        @Override
        public double getWidth() {
            return width;
        }
    }
    

    最后,我们再来创建一个测试类。根据我们上面对类图的分析,相信你一定能写出下面的代码。

    package com.meimeixia.principles.demo2.after;
    
    /**
     * @author liayun
     * @create 2021-05-27 14:49
     */
    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.getWidth() + 1);
            }
        }
    
        // 打印长和宽
        public static void printLengthAndWidth(Quadrilateral quadrilateral) {
            System.out.println(quadrilateral.getLength());
            System.out.println(quadrilateral.getWidth());
        }
    
    }
    

    此时,不妨来运行一下以上测试类,看看打印结果是不是我们所想要的,如下图所示,可以看到长是20,没变,宽是21,因为我们进行了一个扩宽的操作。

    在这里插入图片描述

    大家现在想一想,如果我们去调用resize方法时传入的是一个正方形对象,那么还可不可以呢?肯定是不可以的,因为正方形和长方形它俩现在没有父子关系了,所以在resize方法里面只能传递长方形对象,而不能再传递正方形对象了。这样,我们就通过以上改进完美的解决了案例之前所存在的问题。

    展开全文
  • 胡侃软件设计原则

    千次阅读 2019-06-01 10:23:23
    目录 1. 概述 2. 设计原则 ... 软件设计是从需求规格说明书开始,根据需求分析阶段确定的功能设计软件系统的整体结构、划分功能模块、确定每个模块的实现算法以及编写具体的代码,形成软件最终的软件产...
  • 一些软件设计原则

    万次阅读 2016-05-15 20:21:56
    以前本站向大家介绍过一些软件开发的原则,比如优质代码的十诫和Unix传奇(下篇)中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识,正如我在《再谈“我是怎么招聘程序”》中所说的,一...
  • 1 软件设计模式的七大原则 1.1设计模式的目的 1.2设计模式七大原则 1.3单一职责原则 1.4 接口隔离原则(Interface Segregation Principle) 1.5 依赖倒转原则 1.6 里氏替换原则 1.7 开闭原则 1.8 迪米特法则 ...
  • 软件设计的七大原则——超详细

    千次阅读 2020-04-05 21:33:16
    设计原则是软件设计模式必须尽量遵循的原则,各种原则要求的侧重点不同。其中: 开闭原则是总纲,它告诉我们要对扩展开放,对修改关闭; 里氏替换原则告诉我们不要破坏继承体系; 依赖倒置原则告诉我们要面向接口...
  • 软件设计的七大原则

    万次阅读 2018-12-15 22:07:45
    七大设计原则 开闭原则 依赖导倒置原则 单一职责原则 接口隔离原则 迪米特原则 里氏替换原则 合成复用原则 设计模式-创建型模式 工厂方法模式 抽象工厂模式 建造者模式 单例模式 原型模式 设计模式-...
  • 1. 软件设计模式的概念 软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,...
  • 以前本站向大家介绍过一些软件开发的原则,比如优质代码的十诫和Unix传奇(下篇)中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识,正如我在《再谈“我是怎么招聘程序”》中所说的,一...
  • (转)一些软件设计原则

    千次阅读 2012-09-18 19:55:41
    以前本站向大家介绍过一些软件开发的原则,比如优质代码的十诫和Unix传奇(下篇)中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识,正如我在《再谈“我是怎么招聘程序”》中所说的,一...
  • 绪论本文打算探讨一下软件架构设计的一些设计原则与经过实践验证的设计模式。 前端(MVC模式)和后端(接口层-业务层-助手层)的分层设计经过了几十年大量软件的证明。分层的思想,就是每一个层次专注做一件事情。每...
  • 软件界面设计原则

    千次阅读 2008-04-30 08:57:00
    1.设计原则 (1)用户原则。人机界面设计首先要确立用户类型。划分类型可以从不同的角度,视实际情况而定。确定类型后要针对其特点预测他们对不同界面的反应。这就要从多方面设计分析。(2)信息最小量原则。人机...
  • 以前本站向大家介绍过一些软件开发的原则,比如优质代码的十诫和Unix传奇(下篇)中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识,正如我在《再谈“我是怎么招聘程序”》中所说的,一...
  • 软件设计六大原则

    千次阅读 2011-09-05 12:59:56
    OOP遵照:依赖倒置原则(DIP) 依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。 简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:phpma开源 罗江游鱼 抽象...
  • 这里说的几个软件模式是属于原则层次一级的,比GoF等软件设计模式高一层。遵循这些原则可以使我们设计出来的软件有更好的可复用性和可维护性,同样GoF等软件设计模式也是遵循这一原则的。 下边的条列只是简单的介绍...
  • 软件产品界面设计原则

    千次阅读 2012-02-17 11:19:11
    1.设计原则 (1)用户原则。人机界面设计首先要确立用户类型。划分类型可以从不同的角度,视实际情况而定。确定类型后要针对其特点预测他们对不同界面的反应。这就要从多方面设计分析。 (2)信息最小量原则。人机...
  • 软件架构设计的七大原则

    万次阅读 多人点赞 2019-06-21 22:02:11
    1、开闭原则 开闭原则(Open-Closed Principle, OCP)是指一个软件实体如...闭原则,是面向对象设计中最基础的设计原则。它指导我们如何建立稳定灵活的系统,例如:我们版本更新,我尽可能修改源代码,但是可以增...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,072
精华内容 24,028
关键字:

下面不属于软件设计原则的是