理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?

封装(Encapsulation)
首先,我们来看封装特性。封装也叫作信息隐藏或者数据访问保护。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或者叫函数)来访问内部信息或者数据。

下面这段代码是金融系统中一个简化版的虚拟钱包的代码实现:

public class Wallet {
private String id;
private long createTime;
private BigDecimal balance;
private long balanceLastModifiedTime;
// ...省略其他属性...

public Wallet() {
this.id = IdGenerator.getInstance().generate();
this.createTime = System.currentTimeMillis();
this.balance = BigDecimal.ZERO;
this.balanceLastModifiedTime = System.currentTimeMillis();
}

// 注意:下面对get方法做了代码折叠,是为了减少代码所占文章的篇幅
public String getId() { return this.id; }
public long getCreateTime() { return this.createTime; }
public BigDecimal getBalance() { return this.balance; }
public long getBalanceLastModifiedTime() { return this.balanceLastModifiedTime; }

public void increaseBalance(BigDecimal increasedAmount) {
if (increasedAmount.compareTo(BigDecimal.ZERO) < 0) {
throw new InvalidAmountException("...");
}
this.balance.add(increasedAmount);
this.balanceLastModifiedTime = System.currentTimeMillis();
}

public void decreaseBalance(BigDecimal decreasedAmount) {
if (decreasedAmount.compareTo(BigDecimal.ZERO) < 0) {
throw new InvalidAmountException("...");
}
if (decreasedAmount.compareTo(this.balance) > 0) {
throw new InsufficientAmountException("...");
}
this.balance.subtract(decreasedAmount);
this.balanceLastModifiedTime = System.currentTimeMillis();
}
}

Wallet 类主要有四个属性(也可以叫作成员变量),也就是我们前面定义中提到的信息或者数据。其中,id 表示钱包的唯一编号,createTime 表示钱包创建的时间,balance 表示钱包中的余额,balanceLastModifiedTime 表示上次钱包余额变更的时间。

参照封装特性,对钱包的这四个属性的访问方式进行了限制。调用者只允许通过下面这六个方法来访问或者修改钱包里的数据。String getId()long getCreateTime()BigDecimal getBalance()long getBalanceLastModifiedTime()void increaseBalance(BigDecimal increasedAmount)void decreaseBalance(BigDecimal decreasedAmount)

对于封装这个特性,我们需要编程语言本身提供一定的语法机制来支持。这个语法机制就是访问权限控制。

抽象(Abstraction)
封装主要讲的是如何隐藏信息、保护数据,而抽象讲的是如何隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。

我们利用 Java 中的 interface 接口语法来实现抽象特性。调用者在使用图片存储功能的时候,只需要了解 IPictureStorage 这个接口类暴露了哪些方法就可以了,不需要去查看 PictureStorage 类里的具体实现逻辑:
public interface IPictureStorage {
void savePicture(Picture picture);
Image getPicture(String pictureId);
void deletePicture(String pictureId);
void modifyMetaInfo(String pictureId, PictureMetaInfo metaInfo);
}

public class PictureStorage implements IPictureStorage {
// ...省略其他属性...
@Override
public void savePicture(Picture picture) { ... }
@Override
public Image getPicture(String pictureId) { ... }
@Override
public void deletePicture(String pictureId) { ... }
@Override
public void modifyMetaInfo(String pictureId, PictureMetaInfo metaInfo) { ... }
}

抽象这个概念是一个非常通用的设计思想,并不单单用在面向对象编程中,也可以用来指导架构设计等。而且这个特性也并不需要编程语言提供特殊的语法机制来支持,只需要提供“函数”这一非常基础的语法机制,就可以实现抽象特性、所以,它没有很强的“特异性”,有时候并不被看作面向对象编程的特性之一。

继承(Inheritance)
继承是用来表示类之间的 is-a 关系,比如猫是一种哺乳动物。从继承关系上来讲,继承可以分为两种模式,单继承和多继承。单继承表示一个子类只继承一个父类,多继承表示一个子类可以继承多个父类,比如猫既是哺乳动物,又是爬行动物。

多态(Polymorphism)
多态是指,子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。

public class DynamicArray {
private static final int DEFAULT_CAPACITY = 10;
protected int size = 0;
protected int capacity = DEFAULT_CAPACITY;
protected Integer[] elements = new Integer[DEFAULT_CAPACITY];

public int size() { return this.size; }
public Integer get(int index) { return elements[index];}
//...省略n多方法...

public void add(Integer e) {
ensureCapacity();
elements[size++] = e;
}

protected void ensureCapacity() {
//...如果数组满了就扩容...代码省略...
}
}

public class SortedDynamicArray extends DynamicArray {
@Override
public void add(Integer e) {
ensureCapacity();
int i;
for (i = size-1; i>=0; --i) { //保证数组中的数据有序
if (elements[i] > e) {
elements[i+1] = elements[i];
} else {
break;
}
}
elements[i+1] = e;
++size;
}
}

public class Example {
public static void test(DynamicArray dynamicArray) {
dynamicArray.add(5);
dynamicArray.add(1);
dynamicArray.add(3);
for (int i = 0; i < dynamicArray.size(); ++i) {
System.out.println(dynamicArray.get(i));
}
}

public static void main(String args[]) {
DynamicArray dynamicArray = new SortedDynamicArray();
test(dynamicArray); // 打印结果:1、3、5
}
}

多态也是很多设计模式、设计原则、编程技巧的代码实现基础,比如策略模式、基于接口而非实现编程、依赖倒置原则、里式替换原则、利用多态去掉冗长的 if-else 语句等等。

在上面的例子中,我们用到了三个语法机制来实现多态。第一个语法机制是编程语言要支持父类对象可以引用子类对象,也就是可以将 SortedDynamicArray 传递给 DynamicArray。第二个语法机制是编程语言要支持继承,也就是 SortedDynamicArray 继承了 DynamicArray,才能将 SortedDyamicArray 传递给 DynamicArray。第三个语法机制是编程语言要支持子类可以重写(override)父类中的方法,也就是 SortedDyamicArray 重写了 DynamicArray 中的 add() 方法。

按照What/How/Why 模型梳理:

封装

What:隐藏信息,保护数据访问。
How:暴露有限接口和属性,需要编程语言提供访问控制的语法。
Why:提高代码可维护性;降低接口复杂度,提高类的易用性。

抽象

What: 隐藏具体实现,使用者只需关心功能,无需关心实现。
How: 通过接口类或者抽象类实现,特殊语法机制非必须。
Why: 提高代码的扩展性、维护性;降低复杂度,减少细节负担。

继承

What: 表示 is-a 关系,分为单继承和多继承。
How: 需要编程语言提供特殊语法机制。例如 Java 的 “extends”,C++ 的 “:” 。
Why: 解决代码复用问题。

多态

What: 子类替换父类,在运行时调用子类的实现。
How: 需要编程语言提供特殊的语法机制。比如继承、接口类、duck-typing。
Why: 提高代码扩展性和复用性。

3W 模型的关键在于 Why,没有 Why,其它两个就没有存在的意义。从四大特性可以看出,面向对象的终极目的只有一个:可维护性。易扩展、易复用,降低复杂度等等都属于可维护性的实现方式

你可能感兴趣的:(理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?)