# 面向对象是怎么一回事

**三大基本特征和五大基本原则**

前言    
面向对象的三个特征：**封装、继承、多态**。那么面向对象是怎么体现这三个特征呢？     
关于五大基本原则，这是我找到的对新手（如我）来说还是比较容易理解的表述。

# 面向对象的三大基本特征
## 1. 封装
封装，就是将客观事物抽象为逻辑实体，实体的属性和功能相结合，形成一个有机的整体。并对实体的属性和功能实现进行访问控制，向信任的实体开放，对不信任的实体隐藏。，通过开放的外部接口即可访问，无需知道功能如何实现。

也就是说，封装主要有以下目的：

- 可隐藏实体实现的细节。
- 提高安全性，设定访问控制，只允许具有特定权限的使用者调用。
- 简化编程，调用方无需知道功能是怎么实现的，即可调用。

举个栗子，X怎么跟别人介绍他的女朋友？
```
我的女朋友{  
    属性：  
        姓名;  
        (体重等信息就没必要跟别人说了，隐藏起来)  
    行为：  
        吃饭;  
        (其他的别人也不需要知道，隐藏起来)  
}
```
X只需要跟别人说他女朋友一些公开的属性就行了，其他的可以选择隐藏。

如果别人想约X跟X女朋友出来一起吃个饭，只需要向X发出邀请，而无需知道X是怎么跟X女朋友商量的。

## 2. 继承
**继承**，在继承机制下形成有层级的类，**使得低层级的类可以延用高层级类的特征和方法**。继承的实现方式有两种：实现继承、接口继承。

**实现继承：**直接使用基类公开的属性和方法，无需额外编码。

**接口继承：**仅使用接口公开的属性和方法名称，需要子类实现。

也就是说，继承有以下目的：

- 复用代码，减少类的冗余代码，减少开发工作量。
- 使得类与类之间产生关系，为多态的实现打下基础。

举个栗子，怎么样做到年收千万上亿？继承家产就很棒。
```
X爸{  
    属性：  
        股票;  
        (现金和银行存款作为养老资产，隐藏起来)  
    行为：  
        管理公司;  
}
```
X爸不想努力了，把公司交给X，自己的现金和银行存款已经够养老了。     
```
X 继承 X爸{  
    属性：  
        股票;  
        (自己的现金和银行存款，隐藏起来)  
    行为：  
        管理公司;  
}
```
## 3. 多态
多态，是指一个**类的同名方法，在不同情况下的实现细节不同**。多态机制实现**不同的内部实现结构共用同一个外部接口**。

也就是说，多态有以下目的：

- 一个外部接口可被多个同类使用。
- 不同对象调用同个方法，可有不同实现。

举个栗子，拥有千万资产的X想跟女朋友结婚了。
```
娶亲{  
    行为：  
        领证{挑个黄道吉日去领证};  
        摆酒{宴请各路亲戚三顿饭};  
}
```
X不想像父辈那样专门挑个日子去领证，也不想请一堆不认识的亲戚吃饭。跟未婚妻商量之后，决定这么做。
```
X娶亲 继承 娶亲{  
    行为：  
        领证{跟未婚妻饭后散步，顺便领证};  
        摆酒{仅仅是宴请亲人和两人各自最好的几个朋友};  
}
```
当别人在讨论X是怎么娶亲的时候，可以通过娶亲这个父类，来指向X娶亲子类，这样就可以知道X是怎么娶亲了。

从上述可以看出，多态实现的三个必要条件是：继承、重写（子类继承父类后，对继承的方法重新定义）、父类应用指向子类对象。所以，多态的实现是基于继承的。

# 面向对象的五大基本原则
## 1. 单一职责原则（SRP）
>其核心思想为：一个类，最好只做一件事，只有一个引起它的变化。
一个类，最好有且仅有一个引起它变化的原因。

举个栗子，职员类里包括了普通员工、经理、老板，那类中势必需要用if else来区分判断，而且无论是这三种职员的需求发生变化，都会影响到整个职员类。

按照“单一职责原则”，将普通员工、经理、老板分别建一个类，既不用if else加以区分，也不会在修改某个职员类别的时候影响另一个。

## 2. 开放封闭原则（OCP）
>其核心思想是：软件实体应该是可扩展的，而不可修改的。

一个类，可以扩展（添加属性和功能），但是不要修改已经写好的属性和方法。

>实现开开放封闭原则的核心思想就是对抽象编程，而不对具体编程，因为抽象相对稳定。

打个简单的比方，X的大舅二舅都是他舅，是有血缘关系的舅舅，如果突然冒出来一个跟他有血缘关系的三舅，那也是他舅舅。同时也不能改变他大舅和二舅的亲缘关系。

## 3.里氏替换原则（LSP)
>其核心思想是：子类必须能够替换其基类。
类A是类B的父类，那么在进行调用的时候，类A可以引用类B，但是反过来不行。

其实可以粗糙地理解为，类A就是对外提供一个接口，具体的实现在类B中。

>实现的方法是面向接口编程：将公共部分抽象为基类接口或抽象类，通过Extract Abstract Class，在子类中通过覆写父类的方法实现新的方式支持同样的职责。   

也就是说，其实里氏替换原则是继承和多态的综合体现。

## 4. 依赖倒置原则（DIP)
>其核心思想是：依赖于抽象。具体而言就是高层模块不依赖于底层模块，二者都同依赖于抽象；抽象不依赖于具体，具体依赖于抽象。

在对客观事物抽象成逻辑实体时，可以先思考，同类事物的共性是什么，将这个共性作为这类事物的“高层模块”，若干不同的客观事物作为“底层模块”在依赖”高层“之后，对共性进行特定描述。

举个栗子，苹果跟西瓜都是水果，水果的共同属性是水分、糖分。在这里，”水果“作为高层模块，其属性可以在描述“苹果”和“西瓜”的时候使用，所以“苹果”“西瓜”在此是“底层模块”。

## 5. 接口隔离原则
>其核心思想是：使用多个小的专门的接口，而不要使用一个大的总接口。

接口中定义属性和需要子类实现的方法，实现类必须完全实现接口的所有方法、属性。为什么要接口隔离呢？目的有二：

- 避免引用接口的类，需要实现其实用不到的接口方法、属性。
- 避免当接口修改的时候，有一连串的实现类需要更改。

>分离的手段主要有以下两种：1、委托分离，通过增加一个新的类型来委托客户的请求，隔离客户和接口的直接依赖，但是会增加系统的开销。2、多重继承分离，通过接口多继承来实现客户的需求，这种方式是较好的。

- 委托分离，不直接使用原先的接口，可以用另外增加一个新的接口或类来实现需求。
- 多重继承分离，JDK源码、Spring框架使用了这种方式，后续的JDK源码解析系列会提及，好奇的朋友可以查看集合类的结构。

--END--
