面向对象之特性
A1 总述
封装、抽象、继承、多态
学习思路:
奥义+实现+意义
A2 封装(Encapsulation)
奥义:
-
信息隐藏或数据访问保护
-
类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或函数)来访问内部信息或数据。
实现:
- 通过访问权限控制,比如Java中的访问权限控制语法:private、public等关键字。
- 抽象
意义:
-
使属性和代码内部逻辑不可随意修改,提高diamagnetic可读性和可维护性。
-
类通过有限方法暴露必要的操作,提高类的易用性。
-
降低接口复杂度,提高类的易用性。
解决问题:
- 没有封装的话,因为类属性都暴露,那么调用者要正确操作属性就需要堆业务和每个属性足够了解。
- 相反,我们将属性封装起来,暴露少许几个必要的方法给调用者,那么其不用了解太多业务细节也可使用,同时用错的概率大大下降。
- 就像对冰箱操作,如果有很多按钮,你需要研究很长时间,相反,只有几个必要按钮:开、停、调节温度,则很容易操作且不易出错。
A3 抽象(Abstraction)
奥义:
- 隐藏方法的具体实现
- 让调用者只关心提供的功能,而不用知道这些功能是如何实现的
实现:
- 接口类(如Java中的interface关键字语法)
- 抽象类(如Java中的abstract关键字语法)
Java中长用的interface配合implements
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) { ... } }
意义:
- 让调用者关注功能点而非实现的设计思路从而过滤掉许多非必要信息。
- 对代码设计有重要指导作用,很多设计原则体现了抽象思想,如比如基于接口而非实现编程、开闭原则(对扩展开放、对修改关闭)、代码解耦(降低代码的耦合性)等。
- 降低复杂度,减少细节负担。
解决问题:
- 在方法定义中,抽象化,不暴露过多实现细节从而在改变实现逻辑时不用修改其定义。如
getAliyunPictureUrl()
和getPictureUrl()
。
A4 继承(Inheritance)
奥义:
- 表示类之间的is-a关系,比如猫属于哺乳动物。
- 分类
- 单继承
- 多继承
- 不是所有面向对象语言都支持多继承,如Java、PHP、C#、Ruby等都不支持。
实现:
- 特殊语法机制支持,如Java中extends关键字,C++ 使用冒号(class B : public A),Python 使用 parentheses (),Ruby 使用 <。
意义:
- 代码复用,多个子类重用父类代码,避免代码重复写多遍。
相关问题:
- 继承层次过深会导致代码可读性和可维护性差。
- 耦合度:子父类高耦合,修改父类代码直接影响子类。
建议:
- 多用组合少用继承
A5 多态(Polymorphism)
奥义:
- 子类可以替换父类
实现:
-
用特殊语法机制
-
第一:编程语言要支持父类对象可以引用子类对象
-
第二:编程语言要支持继承
-
第三:编程语言要支持子类可以重写(override)父类中的方法
-
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 } }
-
-
利用接口类
-
public interface Iterator { boolean hasNext(); String next(); String remove(); } public class Array implements Iterator { private String[] data; public boolean hasNext() { ... } public String next() { ... } public String remove() { ... } //...省略其他方法... } public class LinkedList implements Iterator { private LinkedListNode head; public boolean hasNext() { ... } public String next() { ... } public String remove() { ... } //...省略其他方法... } 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); } }
-
Iterator 是一个接口类,定义了一个可以遍历集合数据的迭代器。Array 和 LinkedList 都实现了接口类 Iterator。我们通过传递不同类型的实现类(Array、LinkedList)到 print(Iterator iterator) 函数中,支持动态的调用不同的 next()、hasNext() 实现。
-
-
duck-typing语法
-
class Logger: def record(self): print(“I write a log into file.”) class DB: def record(self): print(“I insert data into db. ”) def test(recorder): recorder.record() def demo(): logger = Logger() db = DB() test(logger) test(db)
-
duck-typing 实现多态的方式非常灵活。Logger 和 DB 两个类没有任何关系,既不是继承关系,也不是接口和实现的关系,但是只要它们都有定义了 record() 方法,就可以被传递到 test() 方法中,在实际运行的时候,执行对应的 record() 方法。
-
意义:
- 提高代码可扩展性和复用性
- 是很多设计模式、设计原则、编程技巧的代码实现基础,比如策略模式、基于接口而非实现编程、依赖倒置原则、里式替换原则、利用多态去掉冗长的 if-else 语句等等。
A6 相关
上集:面向对象之总述
下集:面向对象🆚面向过程
参考文献
极客时间:设计模式之美.王争。