封装(Encapsulation)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| import java.math.BigDecimal;
public class Wallet { private final String id; private final long createTime; private final BigDecimal balance; private long balanceLastModifiedTime;
public Wallet() { this.id = IdGenerater.getInstance().generate(); this.createTime = System.currentTimeMillis(); this.balance = BigDecimal.ZERO; this.balanceLastModifiedTime = System.currentTimeMillis(); }
public String getId() { return this.id; }
public long getCreateTime() { return createTime; }
public BigDecimal getBalance() { return balance; }
public long getBalanceLastModifiedTime() { return 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.substract(decreasedAmount); this.balanceLastModifiedTime = System.currentTimeMillis(); } }
|
抽象(Abstraction)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import java.awt.*;
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) { return null; }
@Override public void deletePicture(String pictureId) {
}
@Override public void modifyMetaInfo(String pictureId, PictureMetaInfo metaInfo) {
} }
|
继承(Inheritance)
继承的概念很好理解,也很容易使用。不过,过度使用继承,继承层次过深过复杂,就会导致代码可读性、可维护性变差。为了了解一个类的功能,我们不仅需要查看这个类的代码,还需要按照继承关系一层一层地往上查看“父类、父类的父类……”的代码。还有,子类和父类高度耦合,修改父类的代码,会直接影响到子类。
所以,继承这个特性也是一个非常有争议的特性。很多人觉得继承是一种反模式。我们应该尽量少用,甚至不用。
多态(Polymorphism)
多态是指,子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| 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]; }
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import java.util.LinkedList;
public interface Iterator { boolean hasNext();
String next();
String remove(); }
public class Array implements Iterator { private String[] data;
@Override public boolean hasNext() { return false; }
@Override public String next() { return null; }
@Override public String remove() { return null; } }
public class Demo { private static void print(Iterator iterator) { while (iterator.hasNext()) { System.out.println(iterator.next()); } }
public static void main(String[] args) { Iterator arrayIterator = new Array(); print(arrayIterator);
Iterator linkedListIterator = new LinkedList(); print(linkedListIterator); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
class Logger: def record(self): print("I write a log info file.")
class DB: def record(self): print("I insert data into db.")
def test(recorder): recorder.record()
def main(): logger = Logger() db = DB() test(logger) test(db)
if __name__ == '__main__': main()
|
多态特性能提高代码的可扩展性和复用性。为什么这么说呢?我们回过头去看讲解多态特性的时候,举的第二个代码实例(Iterator 的例子)。
在那个例子中,我们利用多态的特性,仅用一个 print() 函数就可以实现遍历打印不同类型(Array、LinkedList)集合的数据。当再增加一种要遍历打印的类型的时候,比如 HashMap,我们只需让 HashMap 实现 Iterator 接口,重新实现自己的 hasNext()、next() 等方法就可以了,完全不需要改动 print() 函数的代码。所以说,多态提高了代码的可扩展性。
如果我们不使用多态特性,我们就无法将不同的集合类型(Array、LinkedList)传递给相同的函数(print(Iterator iterator) 函数)。我们需要针对每种要遍历打印的集合,分别实现不同的 print() 函数,比如针对 Array,我们要实现 print(Array array) 函数,针对 LinkedList,我们要实现 print(LinkedList linkedList) 函数。而利用多态特性,我们只需要实现一个 print() 函数的打印逻辑,就能应对各种集合数据的打印操作,这显然提高了代码的复用性。
除此之外,多态也是很多设计模式、设计原则、编程技巧的代码实现基础,比如策略模式、基于接口而非实现编程、依赖倒置原则、里式替换原则、利用多态去掉冗长的 if-else 语句等等。关于这点,在学习后面的章节中,你慢慢会有更深的体会。
小结
关于封装特性
封装也叫做信息隐藏或者数据访问保护。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式来访问内部信息或者数据。它需要编程语言提供权限访问控制语法来支持,例如Java中的private、protected、public关键字。封装特性存在的意义,一方面是保护数据不被随意修改,提高代码的可维护性;另一方面是仅暴露有限的必要接口,提高类的易用性。
关于抽象特性
封装主要讲如何隐藏信息、保护数据,那抽象就是将如何隐藏方法的具体实现,让使用者只需要关心方法提供了哪些功能,不需要知道这些功能是如何实现的。抽象可以通过接口类或者抽象类来实现,但也并不需要特殊的语言机制来支持。抽象存在的意义,一方面是提高代码的可扩展性、维护性,修改实现不需要改变定义,减少代码的改动范围;另一方面,它也是处理复杂系统的有效手段,能有效地过滤掉不必要关注的信息。
关于继承特性
继承是用来表示类之间的is-a关系,分为两种模式:单继承和多继承。单继承表示一个子类只继承一个父类,多继承表示一个子类可以继承多个父类。为了实现继承这个特性,编程语言需要提供特殊的语法机制来支持。继承主要是用来解决代码复用的问题。
关于多态特性
多态是指子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。多态这种特性也需要编程语言提供特殊的语法机制来实现,比如继承、接口类、duck-typing。多态可以提高代码的扩展性和复用性,是很多设计模式、设计原则、编程技巧的代码实现基础。
## 封装 What:隐藏信息,保护数据访问。 How:暴露有限接口和属性,需要编程语言提供访问控制的语法。 Why:提高代码可维护性;降低接口复杂度,提高类的易用性。
##抽象 What: 隐藏具体实现,使用者只需关心功能,无需关心实现。 How: 通过接口类或者抽象类实现,特殊语法机制非必须。 Why: 提高代码的扩展性、维护性;降低复杂度,减少细节负担。
##继承 What: 表示 is-a 关系,分为单继承和多继承。 How: 需要编程语言提供特殊语法机制。例如 Java 的 “extends”,C++ 的 “:” 。 Why: 解决代码复用问题。
##多态 What: 子类替换父类,在运行时调用子类的实现。 How: 需要编程语言提供特殊的语法机制。比如继承、接口类、duck-typing。 Why: 提高代码扩展性和复用性。
3W 模型的关键在于 Why,没有 Why,其它两个就没有存在的意义。从四大特性可以看出,面向对象的终极目的只有一个:可维护性。易扩展、易复用,降低复杂度等等都属于可维护性的实现方式。