面向对象思想和原则
学习Java我猜一些人像我一样都是先学的怎么编码,但是面向对象的思想并未好好思考和学习,今天就让我来带大家学一下面向对象原则和思想
- 面向对象三大特性
封装、继承、多态是面向对象的三大特性。
封装
利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外的接口使其与外部发生联系。用户无需关心对象内部的细节,但可以通过对象对外提供的接口来访问该对象。
优点:
- 减少耦合:可以独立地开发、测试、优化、使用、理解和修改
- 减轻维护的负担:可以更容易被理解,并且在调试的时候可以不影响其他模块
- 有效地调节性能:可以通过剖析来确定哪些模块影响了系统的性能
- 提高软件的可重用性
- 降低了构建大型系统的风险:即使整个系统不可用,但是这些独立的模块却有可能是可用的。
比如一个Person类封装name、gender、age等属性,外界只能通过get()方法获取一个Person对象的name属性和gender属性,而无法获取age属性,但是age属性可以供work()方法使用.
1 public class Person { 2 3 private String name; 4 private int gender; 5 private int age; 6 7 public String getName() { 8 return name; 9 } 10 11 public String getGender() { 12 return gender == 0 ? "man" : "woman"; 13 } 14 15 public void work() { 16 if (18 <= age && age <= 50) { 17 System.out.println(name + " is working very hard!"); 18 } else { 19 System.out.println(name + " can't work any more!"); 20 } 21 } 22 }
继承
继承实现了IS-A关系,例如Cat和Animal就是一种IS-A关系,因此Cat可以继承Animal,从而获得Animal非private的属性和方法。
继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。
Cat可以当做Animal来使用,也就是说可以使用Anima引用Cat对象。父类引用指向子类对象称为向上转型。
1 Animal animal = new Cat();
父类引用 子类对象
多态
多态分为编译时多态和运行时多态;
- 编译时多态主要指方法的重载
- 运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定
运行时多态有三个条件:
- 继承
- 覆盖(重写)
- 向上转型
比如下方的代码中,乐器类(Instrument)有两个子类;Wind和Percussion,它们都覆盖了父类的play()方法,并且在main()方法中使用Instrument来引用Wind和Percussion对象。在Instrument引用调用play()方法,而不是父类Instrument类的方法
1 public class Instrument{ 2 public void play(){ 3 System.out.println("Instrument is playing..."); 4 } 5 }
1 public class Percussion extends Instrument{ 2 public void play(){ 3 System.out.println("Percussion is playing..."); 4 } 5 }
1 public class Wind extends Instrument{ 2 public void play(){ 3 System.out.println("Wind is playing..."); 4 } 5 }
1 public class Music{ 2 public static void main(String[] args){ 3 List<Instrument> instruments = new ArrayList<>(); 4 instruments.add(new Wind()); 5 instruments.add(new Percussion()); 6 for(Instrument instrument : instruments){ 7 instrument.play(); 8 } 9 } 10 }
Wind is playing...
Percussion is playing
设计原则:
S.O.L.I.D
1. 单一责任原则
修改一个类的原因应该只有一个。
换句话说就是让一个类只负责一件事,当这个类需要做很多事情的时候,就需要分解这个类。
如果一个类承担的职责过多,就等于把这些职责耦合在了一起,一个职责的变化可能会削弱对这个类完成其他职责的能力。
2. 开放封闭原则
类应该对扩展开放,对修改关闭。
扩展就是添加新功能的意思,因此该原则要求在添加新功能时不需要修改代码。
符合开闭原则最典型的设计模式是装饰者模式,它可以动态地将责任附加到对象上,而不用去修改类的代码。
3. 里氏替换原则
子类对象必须能够替换掉所有父类对象。
继承是一种IS-A关系,子类需要能够当成父类来使用,并且需要比父类更特殊。
如果不满足这个原则,那么各个子类的行为上就会有很大的差异,增加继承体系的复杂度。
4. 接口分离原则
不应该强迫客户依赖于它们不用的方法。
因此使用多个专门的接口比使用单一的总接口要好。
5. 依赖倒置原则
高层模块不应该依赖于低层模块,二者都应该依赖于抽象;
抽象不应该依赖于细节,细节应该依赖于抽象。
高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于低层模块,那么低层模块的改动就会影响到高层模块,从而迫使高层模块也需要改动。
依赖于抽象意味着:
任何变量都不应该持有一个指向具体类的指针或者引用;
任何类都不应该从具体类派生;
任何方法都不应该覆写它的任何基类中的已经实现的方法。
其他常见原则
在实际开发中还有下面这些常见的设计原则。
1. 迪米特法则
迪米特法则又叫作最少知识原则,就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。
2. 合成复用原则
将尽量使用对象组合,而不是通过继承来达到复用的目的。
3. 共同封闭原则
一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。
4. 稳定抽象原则
最稳定的包应该是最抽象的包,不稳定的包应该是具体的包,即包的抽象程度跟它的稳定性成正比。
5. 稳定依赖原则
包之间的依赖关系都应该是稳定方向依赖的,包要依赖的包要比自己更具有稳定性。
参考:CS-Notes