一直谈软件设计,却不能准确的描述。结合最近看《黑客与画家》,这才对设计的六大原则有了一点浅显的体会。首先说一下一个项目的路径:开发、重构、测试、投产、运维。其中重构的好处就是希望对原有设计和代码进行修改(注意:重构的应该分两个方向:设计上的修改和代码上的修改),而运维则是希望尽量减少对原有代码的修改,保持历史代码的纯净,提高系统稳定性。
原则一:开闭原则(OCP)
软件应该保持对扩展开放,对修改关闭。开发的时候要允许在已有软件模块的基础上进行拓展功能,并尽可能不去修改已有的功能模块。换句话说就是一个软件实体应该通过扩展来实现变化,而不是通过修改已有代码来实现变化。
那么如何使用这个原则呢?个人认为核心是找到或者预见未来可能发生变化代码块。在具体使用的时候可以考虑:
1、通过使用接口或者抽象类约束扩展,并对扩展的边界进行限定,保证出现在接口或抽象类中方法都是有用的方法。
2、参数类型尽可能是接口或者抽象类,而不是某一个实现类。
3、合理的设计抽象类和接口,也就是尽可能的保持抽象层的稳定,一经确定不允许修改。
4、尽可能的通过元数据来控制模块的行为。简单点说通过配置参数,来控制模块的模块的功能。参数既可以写在普通文件中,也可以保存在数据库中。提到这一点就不得不说Spring容器,Spring就是一个典型的通过元数据来控制模块行为。仅仅通过元数据控制模块是不够的,用到极致的是IOC,也就是控制反转。
5、统一项目约束,团队中的所有人员必须遵守项目约束,比如说命名规则等。(团队合作中非常重要)
6、封装变化,将相同的变化封装到同一个接口或者抽象类中,不同的变化封装到不同 的接口或者抽象类中。也就是多种变化不能共存。
值得注意的是,OCP并不意味着完全不修改已有代码。通常而言,底层模块的变化,必须服务于高层模块,也就是耦合。在具体使用中需要根据情况考虑。
原则二:里氏替换原则(LSP)
开发时,基类型(basetype)能够被子类型(subtype)替代,充分考虑多态性。换句话说,一个软件实体如果使用的是一个基类,那么一定适用于其子类,而且无法差别到底是基类对象还是子类对象。比如说有两个类,一个是Base类,另一个Sub类继承了Base子类。那么如果一个方法可以接收一个基类对象b:method(Base b),那么他必然可以接收一个子类对象s,也就是method(s)。这样一看,LSP其不是很简单么?当然不是!LSP本质上应该是说在 继承结构构建的过程中,要合理,而不是滥用!!也就是说不是所有的继承都是合理的。这里我们也来说说经典的问题:”正方形不是矩形“。在现实世界中,正方形是矩形。在OO中,正方形和矩形的也应该是IS-A的关系,这关系正好是OO中的继承关系的依据嘛。因此,在设计类的时候,正方形Square类应该继承矩形Rectangle类。
</pre><pre class="java" name="code">package com.ldd.lsp; public class Rectangle { private int width; private int height; public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } }
package com.ldd.lsp; public class Square extends Rectangle { private int width; private int height; public void setWidth(int width) { this.width=width; } public void setHeight(int height) { this.height=height; } }
package com.ldd.lsp; public class Client { public Rectangle addHeight(Rectangle b){ while(b.getHeight()<b.getWidth()){ int height = b.getHeight(); b.setHeight(height++); } return b; } }
package com.ldd.lsp; public class Test { public static void main(String[] args) { Client client=new Client(); Rectangle rectangle=new Rectangle(); rectangle.setHeight(5); rectangle.setWidth(9); //LSP原则 Rectangle square=new Square(); square.setHeight(5); square.setWidth(5); client.addHeight(rectangle); //正方形到底是不是矩形呢? client.addHeight(square); } }