一、三大特性
封装
封装是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外的接口使其与外部发生联系。通过封装能够让对象使用者无需关心对象内部的细节,同时又可以通过对象对外提供的接口来访问该对象。
封装的优点包括:
- 减少耦合:可以独立地开发、测试、优化、使用、理解和修改。
- 减轻维护的负担:可以更容易得被理解,并且在调试的时候可以不影响其他模块。
- 有效地调节性能:可以通过剖析来确定哪些模块影响了系统的性能。
- 提高软件的可重用性
- 降低构建大型系统的风险:即使整个系统不可用,但是这些独立的模块有可能是可用的。
以Worker类为例,Worker类中封装了id,name,gender,age等属性,类外部仅可以通过get方法获取其id,name,gender属性,而无法获得age这个属性,但是可以在内部的isAble方法中使用。
可以看到,gender属性使用int数据类型进行存储,封装使得用户注意不到这种实现细节。同时,如果gender属性的数据类型需要修改,也不会影响到客户端代码的正常运行。
public class Person {
private int id;
private String name;
private int gender;
private int age;
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getGender() {
return gender == 0 ? "man" : "woman";
}
public void work() {
if (18 <= age && age <= 50) {
System.out.println(name + " is able to work!");
} else {
System.out.println(name + " can't work any more!");
}
}
}
继承
继承实现了IS-A关系,例如Dog和Animal就是一种IS-A关系,因此Dog可以继承自Animal,从而获得Animal类中的非private属性和方法。
继承应该遵循里氏替换原则,即子类对象必须能够替换掉父类对象。
具体来说,Dog可以当做Animal来使用,也就是说可以使用Animal来引用Cat对象。父类引用指向子类对象称之为向上转型。
Animal animal = new Dog();
多态
多态分为编译时多态和运行时多态。编译时多态主要指方法的重载;运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定(向上转型)。
运行时多态有三个条件:
- 继承
- 覆盖(重写)
- 向上转型
以Instrument类为例,其包括两个子类:Wind和Percussion,他们都覆盖了父类的play方法,并且在main方法中使用父类Instrument来引用Wind和Percussion对象。在Instrument引用调用play方法时,会执行实际引用对象所在类的play方法,而不是Instrument类的方法:
public class Instrument {
public void play() {
System.out.println("Instument is playing...");
}
}
public class Wind extends Instrument {
public void play() {
System.out.println("Wind is playing...");
}
}
public class Percussion extends Instrument {
public void play() {
System.out.println("Percussion is playing...");
}
}
public class Music {
public static void main(String[] args) {
List<Instrument> instruments = new ArrayList<>();
instruments.add(new Wind());
instruments.add(new Percussion());
for(Instrument instrument : instruments) {
instrument.play();
}
}
}
运行结果输出如下:
Wind is playing...
Percussion is playing...
二、设计原则
S.O.L.I.D
简写 | 全拼 | 中文翻译 |
---|---|---|
SRP | The Single Responsibility Principle | 单一责任原则 |
OCP | The Open Closed Principle | 开放封闭原则 |
LSP | The Liskov Substitution Principle | 里氏替换原则 |
ISP | The Interface Segregation Principle | 接口分离原则 |
DIP | The Dependency Inversion Principle | 依赖倒置原则 |
1.单一责任原则
单一责任原则是指让一个类只负责一件事。如果这个类需要做过多事情的时候,就需要分解这个类。
如果一个类承担的职责过多,就等于把这些职责耦合在了一起。一个职责的变化可能会削弱这个类完成其他职责的能力。
2.开放封闭原则
开放封闭原则是指类应该对扩展开放,对修改关闭。要求在添加新功能时不需要修改类内部的代码。
符合开闭原则最典型的设计模式是装饰器模式,该模式可以动态地将责任附加到对象上,而不用去修改类的代码。
3.里氏替换原则
里氏替换原则是指子类对象必须能够替换掉所有父类对象,子类需要能够当成父类来使用,并且需要比父类更加特殊。
4.接口分离原则
接口分离原则是指不应该强迫客户依赖于它们不用的方法,因此使用多个专门的接口比使用单一的总接口要好。
5.依赖倒置原则
依赖倒置原则是指高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。
高层模块包含一个应用程序中需要的策略选择和业务模块,如果高层模块依赖于低层模块,那么低层模块的改动就会直接影响到高层模块,从而迫使高层模块也要改动。
依赖于抽象则意味着:
- 任何变量都不应该持有一个指向具体类的指针或者引用;
- 任何类都不应该从具体类派生;
- 任何方法都不应该覆写它的任何基类中的已经实现的方法。
其他常见原则
除了上述的经典原则,在实际开发中还有下面这些常见的设计原则。
简写 | 全拼 | 中文翻译 |
---|---|---|
LOD | The Law of Demeter | 迪米特法则 |
CRP | The Composite Reuse Principle | 合成复用原则 |
CCP | The Common Closure Principle | 共同封闭原则 |
SAP | The Stable Abstractions Principle | 稳定抽象原则 |
SDP | The Stable Dependencies Principle | 稳定依赖原则 |
1. 迪米特法则
迪米特法则又叫作最少知识原则(Least Knowledge Principle,简写 LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。
2. 合成复用原则
尽量使用对象组合,而不是通过继承来达到复用的目的。
3. 共同封闭原则
一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。
4. 稳定抽象原则
最稳定的包应该是最抽象的包,不稳定的包应该是具体的包,即包的抽象程度跟它的稳定性成正比。
5. 稳定依赖原则
包之间的依赖关系都应该是稳定方向依赖的,包要依赖的包要比自己更具有稳定性。