面向对象设计的基本原则学习体会
____程序员的艺术:S.O.L.I.D原则
(SOLID原则是由罗伯特.C.马丁提出的)
1. 单一责任原则(SRP);
TheSingle Responsibility Principle
概念:对象应该仅具有一种单一功能
详解:当需要修改某个类的时候原因有且只有一个,即一个类只做一种类型责任,当这个类需要承担其他类型的责任的时候,需要分解这个类。
总结评价:最容易理解的原则,同时也是最容易违反的原则
2. 开放封闭原则(OCP);
The Open Closed Principle
概念:软件体应该是对于扩展是开放的,但对于修改是封闭的
详解:扩展开放(有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况);修改封闭(类一旦设计完成,就不要对类进行任何修改,让其独立完成工作)
即通过增加代码来扩展功能,而不是修改已经存在的代码;
总结评价:此原则最抽象、最难理解。
实例:
public boolean sendByEmail(String addr, String title, String content) { } public boolean sendBySMS(String addr, String content) { } // 在其它地方调用上述方法发送信息 sendByEmail(addr, title, content);
sendBySMS(addr, content);
如果现在又多了一种发送信息的方式,比如可以通过QQ发送信息,那么不仅需要增加一个方法sendByQQ(),还需要在调用它的地方进行修改,违反了OCP原则,更好的方式是
抽象出一个Send接口,里面有个send()方法,然后让SendByEmail和SendBySMS去实现它既可。这样即使多了一个通过QQ发送的请求,那么只要再添加一个SendByQQ实现类实现Send接口既可。这样就不需要修改已有的接口定义和已实现类,很好的遵循了OCP原则。
3. 里氏替换原则(LSP);
概念:程序中的对象应该是可以再不改变程序正确性的前提下被它的子类所替换的(当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有is-A关系)。
详解:子类应该可以以替换任何基类能够出现的地方,并且经过替换后,代码还能正常工作。另外,在代码中不应该出现If/else之类对子类类型进行判断的条件。该原则是使代码符合开闭原则的一个重要保证。正是由于子类类型的可替换性才使得父类型的模块在无需修改的情况下就可以扩展。
总结评价:该原则听起来好像是那么回事,不过自己还是有一些懵。
实例:
public class Rectangle {
private double width;
private double height;
public void setWidth(doublevalue) {
this.width = value;
}
public double getWidth() {
return this.width;
}
public void setHeight(doublevalue) {
this.width = value;
}
public double getHeight() {
return this.height;
}
public double Area() {
return this.width*this.height;
}
}
public class Square extendsRectangle {
/* 由于父类Rectangle在设计时没有考虑将来会被Square继承,所以父类中字段width和height都被设成private,在子类Square中就只能调用父类的属性来set/get,具体省略 */
}
// 测试
voidTestRectangle(Rectangle r) {
r.Weight=10;
r.Height=20;
Assert.AreEqual(10,r.Weight);
Assert.AreEqual(200,r.Area);
}
// 运行良好
Rectangle r = newRectangle ();
TestRectangle(r);
// 现在两个Assert测试都失败了
Square s = newSquare();
TestRectangle(s);
我们得出一个非常重要的结论:一个模型,如果孤立地看,并不具有真正意义上的有效性,模型的有效性只能通过它的客户程序来表现。例如孤立地看Rectangle和Squre,它们时自相容的、有效的;但从对基类Rectangle做了合理假设的客户程序TestRectangle(Rectangle r)看,这个模型就有问题了。在考虑一个特定设计是否恰当时,不能完全孤立地来看这个解决方案,必须要根据该设计的使用者所作出的合理假设来审视它。
4. 接口分离原则(ISP);
概念:不能强迫用户去依赖那些他们不使用的接口。即使用多个专门的接口比使用单一的总接口要好。
详解:客户端不应该依赖大的接口,应该裁剪为晓得接口给客户端模块使用,以减少依赖性。如java中实现多个接口,不同的接口给不用的客户模块使用,而不是提供给客户模块一个大的接口。
总结评价:理解起来很简单,就是让我们根据不同的功能,多建立接口。
实例:
public interface Animal {
public void eat(); // 吃
public void sleep(); // 睡
public void crawl(); // 爬
public void run(); // 跑
}
public class Snake implements Animal {
public void eat() {
}
public void sleep() {
}
public void crawl() {
}
public void run(){
}
}
public class Rabit implements Animal {
public void eat() {
}
public void sleep() {
}
public void crawl() {
}
public void run(){
}
}
Snake并没有run的行为而Rabbit并没有crawl的行为,而这里它们却必须实现这样不必要的方法,更好的方法是crawl()和run()单独作为一个接口,这需要根据实际情况进行调整,反正不要把什么功能都放在一个大的接口里,而这些功能并不是每个继承该接口的类都所必须的。
5. 依赖反转(倒置\注入)原则(DIP);
概念:一个方法应该依赖于抽象而不是实例
详解:高层模块不应该依赖于底层模块,二者都应该依赖于抽象;
抽象不应该依赖于细节,细节应该依赖于抽象;类可能依赖于其他类来执行其工作。但是,他们不应当依赖于该类的特定具体实现,而应当是他的抽象;如果类只关心它们用于支持特定契约而不是特定类型的组件,就可以快速而轻松地修改这些低级服务的功能,同时最大限度地降低对系统其余部分的影响。
总结评价:此原则不仅仅可以用在编程中,我们这回的分工化,标准化都是这个设计原则的体现。
总结:
1. 一个对象只承担一种责任,所有服务接口只通过它来执行这种任务。
2. 程序实体,比如类和对象,向扩展行为开放,向修改行为关闭。
3. 子类应该可以用来替代他所继承的类。
4. 一个类对另一个累的依赖应该限制在最小化的接口上。
5. 依赖抽象层(接口),而不是具体类。
以上原则是非常基础而且重要的面向对象原则,但要理解,融会贯通这些原则,需要足够的经验和知识的积累。还有其他的一些很重要的原则,等待我去探索和发掘。