什么是面向对象
- 面向对象编程是一种编程范式或编程风格。它以类或对象作为组织代码的基本单元,并将封装、抽象、继承、多态四个特性,作为代码设计和实现的基石。
面向对象编程语言是支持类或对象的语法机制,并有现成语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言。
什么是面向对象编程语言?
1、支持类或对象的语法机制 2、有现成的语法机制,能方便的实现 封装、抽象、继承、多态
面向对象软件开发的三个阶段
- OOP 面向对象编程 (Object Oriented Programming)
- OOA 面向对象分析(Object Oriented Analysis)
- OOD 面向对象设计 (Object Oriented Design)
UML(Unified Model Language)统一建模语言
封装、继承、抽象、多态
封装(Encapsulation)
封装也叫作信息隐藏或者数据访问保护
类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或者叫函数)来访问内部信息或者数据
比如说某个类的成员变量 不应该暴露出的set 或者 get 方法 使用具体有意义的函数,提供出操作成员变量的方式。同时保证成员变量之间的逻辑一致性。 比如说我们一个虚拟钱包代码如下:
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();
}
}
那么封装的意义是什么?它能解决什么编程问题?
- 给类中的属性做访问限制,能够提高代码的可读性,可维护性。避免因为随意的操作导致属性逻辑上的不一致,比如说修改了账户余额,而没有修改账户的更新时间。
- 类仅仅通过有限的方法暴露必要的操作,也能提高类的易用性。比如给你一个非常多按键的遥控器,和给你一个只有必要按钮的遥控器。你一眼可以知道该如何操作,而且操作出错的概率也会降低很多。
抽象
封装主要讲的是如何隐藏信息,保护数据,而抽象讲的是如何隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的。
在面向对象编程中,我们常借助编程语言提供的接口类,或者抽象类这两种语法机制,来实现抽象这一特性。 但是并不一定必须这种语法机制,只需要提供“函数”这一非常基础的语法机制,就可以实现抽象特性。
比如图片存储的这一功能
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(),那么即便修改了内部存储方式,我们也需要修改命名。
继承
继承用来表示类之间的is-a关系,提高了代码的复用能力。但是多继承可能导致代码的逻辑混乱,用好继承对编码能力提出了更高的要求,java语言不支持多继承。
继承存在的意义是什么?它能解决什么编程问题?
- 继承最大的好处就是代码复用。当然我们也可以通过其他方式来解决这个代码复用的问题,比如利用组合关系为不是继承关系。
- 从人类认知的角度上来说,继承是一种is-a关系,通过继承来关联两个类,反映真实世界中的这种关系,非常符合人类的认知,从设计角度来说,也是一种结构美感。
- 继承层次过深过复杂,就会导致代码可读性、可维护性变差。子类和父类高度耦合,修改父类代码会直接影响到子类。多用组合少用继承的设计思想
多态
多态是指,子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。 上代码:
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
}
}
多态这种特性也需要编程语言提供特殊的语法机制来实现。在上面的例子中,我们用到了三个语法机制来实现多态。
- 第一个语法机制是编程语言要支持父类对象可以引用子类对象,也就是可以将 SortedDynamicArray 传递给 DynamicArray。
- 第二个语法机制是编程语言要支持继承,也就是 SortedDynamicArray 继承了 DynamicArray,才能将 SortedDyamicArray 传递给 DynamicArray。
- 第三个语法机制是编程语言要支持子类可以重写(override)父类中的方法,也就是 SortedDyamicArray 重写了 DynamicArray 中的 add() 方法。 利用接口类来实现多态特性:
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);
}
}
Java 这样的静态语言,通过继承实现多态特性,必须要求两个类之间有继承关系,通过接口实现多态特性,类必须实现对应的接口。
多态特性存在的意义是什么?它能解决什么编程问题?
- 多态特性能提高代码的可扩展性和复用性
- 多态是很多设计模式、设计原则、编程技巧的代码实现基础,比如策略模式,基于接口而非实现编程,依赖倒置原则,里氏替换原则、利用多态去掉冗长的if-else语句等等。