## 連載:面向對象葵花寶典:思想、技巧與實踐(11) - “封裝” 詳解
封裝的概念本身很好理解,意思就是把一堆東東裝起來。
但要想真正理解封裝,這樣還遠遠不夠。
第一個問題是:我們要封裝什么?
這個問題很好回答,封裝當然是封裝不想讓別人知道或者看到的東東了。
例如:
你的身家。。。。。。
漂亮MM的年齡。。。。。。
富二代的女朋友數。。。。。
明星是否整過容。。。。。。
你是如何賺到100萬的(想想什么樣的賺錢方法不想讓人知道?)。。。。。。
你是如何消磨時間的(想想什么樣的消磨時間方法不想讓人知道?)。。。。。。
等等
站在面向對象的角度來說,封裝就是“類”的一個功能,你可以封裝“類”的屬性(比如身家、年齡等),也可以封裝“類”的方法(比如說如何賺錢、如何消磨時間),也就是說,面向對象通過“類”來實現了封裝。
??
第二個問題是:我們為什么要封裝?
封裝數據的主要原因是“**保護隱私**”,因為有“隱私”,所以要“封裝”。如果沒有封裝,你的隱私就暴露在所有人面前了,別人也可以控制你的隱私,那樣將是非常危險的。
?
例如面向過程的設計中,數據結構是公開的,任何能夠獲取到數據的人都可以隨意修改,也可以使用不同的方式修改,如果某個不小心的程序員或者菜鳥或者一個準備離職又心懷不滿的開發人員,無意或有意改錯了,那么其它依賴這個數據的函數都會受到影響,要么導致業務出錯,甚至導致程序崩潰。
?
而面向對象的類封裝了屬性后,對屬性的修改只能通過類的方法進行,一來不會暴露內部的具體屬性,二來對屬性的操作都是統一的,不會出現亂改的情況。
?
封裝方法的主要原因是“**隔離復雜度**”。每個類只需要關注自己的負責的功能如何完成即可,如果需要其它類配合,只需要調用類的方法即可,而不需要了解其它類功能的具體的實現。
?
“隔離復雜度”的例子即使在現實世界中也比比皆是。例如我們夏天常用的空調能夠提供制冷功能,我們只要輕輕一按遙控器的按鈕,空調就能夠開始制冷,但空調究竟是如何制冷的,絕大部分人并不知道,也并不關心。空調封裝了制冷的實現過程,對人提供了一個制冷的按鈕,人通過這個按鈕來啟動制冷的過程。
?
【封裝的樣例】
簡單的這么回答你可能沒有什么感覺,但給一個面向對象“封裝”和面向過程“無封裝”的例子,相信你就很清楚了。
?舉個簡單的例子,假設用程序實現付款這個操作,我們來看面向對象和面向過程的方式。
?
面向過程?=?算法?+?數據結構
這里的數據結構是公開的,每個地方都可以看到和引用的,而且必須知道,否則面向過程的各個處理流程就沒法處理了。
具體代碼實現如下:
person.h
~~~
#ifndef PERSON_H_
#define PERSON_H_
typedef struct Person{
char* name; //姓名
int money; //金錢數量
}Person;
#endif /* PERSON_H_ */
~~~
money.c
~~~
#include <stdio.h>
#include <stdlib.h>
#include "person.h"
#define null 0x00
int main(void) {
Person* me = (Person*)malloc(sizeof(Person));
me->name = "華仔";
me->money = 100;
//收銀員收銀
if( me->money > 50 ){
me->money -= 50; //收銀操作時,需要知道Person實際有多少錢,然后直接操作Person的money
printf("%s 付款成功,共 %d 塊\n", me->name, me->money);
}
else{
printf("%s 付款失敗 \n", me->name);
}
//小偷偷錢
printf("偷了 %s %d 塊\n", me->name, me->money);
me->money = 0; //小偷也可以直接知道Person有多少錢,并直接操作Person的money
free(me);
me = null;
return EXIT_SUCCESS;
}
~~~
也就是說,你有多少錢,所有人都知道!
也就是說,你的錢,別人可以隨便控制!
就像你去超市買東西,付款的時候,收銀員說:把你所有的錢擺在臺子上,我從里面拿50.
不用我說你也知道,這當然是一件很恐怖的事情!
面向對象?=?對象?+?交互
但在面向對象的實現里面就不會這樣了,你不需要把錢擺出來,你只需要拿出50就可以了,你既不需要擔心別人知道你有多少錢(然后實施某種后續動作),也不需要擔心別人拿錯了(不管是有意的還是無意的)。
具體實現代碼如下:
Person.java
~~~
package com.oo.java;
/**
* 普通人
*/
public class Person {
private String _name; //姓名
private Integer _money; //金錢
public String getName() {
return _name;
}
public Boolean pay(Integer money){
//付款的邏輯由Person自己控制,例如判斷當前的錢是否夠支付
if( money > _money ){
return false;
}
_money -= money;
return true;
}
}
~~~
Cashier.java
~~~
package com.oo.java;
/**
* 收銀員
*/
public class Cashier {
public Boolean get(Person person, Integer money){
Boolean result = person.pay(money); //收銀員只需要調用Person的pay方法,無需知道Person當前有多少錢
if( result ){
System.out.println(person.getName() + " 付款成功,共 " + money +" 塊");
}
else{
System.out.println(person.getName() + " 付款失敗");
}
return result;
}
}
~~~
Thief.java
~~~
package com.oo.java;
/**
* 小偷
*/
public class Thief {
public Boolean stole(Person person){
//小偷想直接訪問Person的money,但這樣做不允許,編譯出錯
System.out.println("偷了 " + person.getName() +" " + person._money + " 塊\n");
person._money = 0;
//雖然小偷這個類語法上也可以直接調用Person的pay方法,
//但Person的pay方法里面可以做很多的校驗功能來讓這樣的調用返回失敗
person.pay(50);
}
}
~~~
- 前言
- (1) - 程序設計思想的發展
- (2) - 面向對象語言發展歷史
- (3) - 面向過程 vs 面向對象
- (4) - 面向對象是瑞士軍刀還是一把錘子?
- (5) - 面向對象迷思:面向對象導致性能下降?
- (6) - 不要說你懂“類”
- (7) - “對象”新解
- (8) - “接口” 詳解
- (9) - “抽象類” 詳解
- (10) - “抽象” 詳解
- (11) - “封裝” 詳解
- (12) - “繼承” 詳解
- (13) - “多態” 詳解
- (14) - 面向對象開發技術流程
- (15) - 需求詳解
- (16) - 需求分析終極目的
- (17) - 需求分析518方法
- (18) - 用例分析
- (19) - 功能點提取
- (20) - 用例圖的陷阱
- (21) - SSD
- (22) - 領域模型
- (23) - 領域建模三字經
- (24) - 設計模型
- (25) - 類模型
- (26) - 類模型三板斧
- (27) - 動態模型設計
- (28) - 設計原則:內聚&耦合
- (29) - 高內聚低耦合
- (30) - SRP原則
- (31) - OCP原則
- (32) - LSP原則
- (33) - ISP原則
- (34) - DIP原則
- (35) - NOP原則
- (36) - 設計原則如何用?
- (37) - 設計模式:瑞士軍刀 or 錘子?
- (38) - 設計模式之道
- (39) - 設計原則 vs 設計模式
- (40) - DECORATOR模式
- (完)- 書籍已經出版