面向對象到底有什麼(上)?

我們很多時候在使用高級程序設計語言進行軟件開發的時候,必然會遇到”OOP“,”OO“,”面向對象“等字眼。那麼這些名詞都是什麼意思呢,在代碼中是如何來體現的呢?我們高級程序設計語言和我們以前遇到的程序設計語言有什麼區別呢,優劣勢都是什麼?我是覺得,瞭解了這些,就能理清我們現在的工作。(以C++爲例)

1 . 低級程序設計語言(以C爲例)

C語言相對於C++其實是低級程序設計語言,我們在使用C語言的時候,主要是利用C的簡潔、允許直接訪問物理地址,對硬件進行操作、表達方式靈活、數據類型豐富等特點。但是,一套成型的桌面軟件應當是由界面、事件響應、後臺運算等組成,邏輯操作紛繁複雜,C語言是一種逐過程的設計語言,面對任務,把任務進行分解成所需要的步驟,然後編寫函數一步一步的實現即可。

比如需要完成一個象棋的遊戲,需要按照一般遊戲的玩法,構建程序的流程。初始化棋盤->初始化玩家1和玩家2->玩家1下棋->刷新棋盤->判斷是否成功->玩家2下棋->刷新棋盤->判斷是否成功->···->玩家1(或玩家2)成功->初始化棋盤。可以看出來,這是一個贏得過程,程序從頭到尾都在Run下棋算法。如果針對玩家,還有一些統計數據、排行榜、歷史信息等需要添加,或者針對整個局面需要”悔棋“,可能需要在整個程序都需要修改。這樣看來,程序的可擴展性不強,程序的邏輯稍微有點混亂。

2 . 高級程序設計語言

而,高級程序設計語言的優勢,在於對面向對象的支持。同樣是一個象棋遊戲,我們首先分析一下象棋遊戲的元素都有什麼。”象棋“、”玩家“、”棋盤“、”界面“、”統計系統“等等。我們可以明顯的看出來,這些元素都是組成一個系統的部分,每一個元素都是一個實體,都有實實在在的存在意義。並且,每一個元素都有的特性。比如象棋,貌似可以被移動,還有多種不同的形態(車、馬、卒···),可以走不同的路線,比如玩家,能下棋,能悔棋,能放棄,比如棋盤,有自己的形式。

當我們把這些實實在在的實體抽象出來之後,就成了一個比較通用的對象的集合,我們稱之爲類。因爲具有象棋的這些特性的,我們都可以認爲它是個”象棋“,都是一類東西。而功能的實現,只需要我們去拼湊對象來完成一個一個功能即可。並且我們需要添加新的功能,分析功能,找到涉及到的類,添加相應的功能接口,在相應的地方調用,即可完成。


2.1 封裝

(以程序員的角度)從上邊可以看出來一件事情,就是我們在完成某個功能的時候,僅僅需要在”用到的“地方”調用“相應的函數即可。也就是說,如果這個類不是我們自己的寫的的話,我們可能根本不知道里邊究竟寫了哪些內容,即使是自己寫的,也許時間久了,就忘記了函數是如何實現的,但是最最重要的,我們知道如何去用,就可以了,這個就是我們經常說的”面向對象的三大特性之一“的”封裝性“。換言之,設計類的人員使用訪問修飾符(private等)向外部使用人員屏蔽了內部的實現細節,使得我們能專注於邏輯代碼的實現,以及後期比較容易debug。

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 類的封裝
class Person{

public:
	Person();
	~Person();

private:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
};

Person::Person()
{
	//
}

Person::~Person()
{
	//
}
請看上邊的例子,這是一個簡單的“Person”類,裏邊有構造函數Person(),析構函數~Person(),還有三個“私有的”成員變量,已經三個“公有的”成員函數。

我們將這個類再稍微完善一下,在使用這個簡單的類的時候,是這樣的:

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 類的封裝
class Person{

public:
	Person(int age, char gender, std::string name)
		:_age(age),
		 _gender(gender),
		 _name(name) 
	{}

	~Person();

private:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
};

Person::~Person(){
	//
}



int main() {

	// 聲明一個Person類型的對象
	// 請注意:對象的概念在這裏開始出現,因爲Person是一個集合,一個類型,那麼類型的一個一個實際的例子,就是對象
	Person m_person(25, '男', "chengzhen");

	// 獲得年齡
	m_person.get_age();

	// 獲得性別
	m_person.get_gender();

	// 獲得姓名
	m_person.get_name();

	std::cin.get();
}

我們爲類的構造函數添加了形參,目的是在聲明的時候,同時可以傳入的參數直接在初值列中賦值給Person類的成員變量(這種給成員變量初始化的方式也是比較推崇的,可以看看Effective C++這本書)。然後我們就可以在需要的地方拿到年齡、性別、姓名了。但是我們發現在使用的時候,只是需要調用一下get_age()、get_gender()、get_name()等接口函數,乍一看,我是不知道這個函數究竟是怎麼拿到這些成員變量的。但是假設我們這樣寫:

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 類的封裝
class Person{

public:
	Person(int age, char gender, std::string name)
		:_age(age),
		 _gender(gender),
		 _name(name) 
	{}

	~Person();

public:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
};

Person::~Person(){
	//
}



int main() {

	// 聲明一個Person類型的對象
	// 請注意:對象的概念在這裏開始出現,因爲Person是一個集合,一個類型,那麼類型的一個一個實際的例子,就是對象
	Person m_person(25, '男', "chengzhen");

	// 獲得年齡
	m_person._age;
	m_person.get_age();

	// 獲得性別
	m_person._gender;
	m_person.get_gender();

	// 獲得姓名
	m_person._name;
	m_person.get_name();

	std::cin.get();
}

可以看見,我們在類的設計中,將“成員變量”的訪問等級變成了“public”,然後在入口函數中,m_Person類可以直接調用_age,_gender,_name。也就是說,成員函數被完全暴露給了使用者,更有甚者,使用的人可以隨意修改成員變量,那麼“封裝性”的意義就被削弱了。正確的做法是:如果成員變量有被修改的必要,那麼可以在成員函數中,添加了可以間接修改的函數。如果沒有被修改的可能,那麼就只給讀的權限。

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 類的封裝
class Person{

public:
	Person(int age, char gender, std::string name)
		:_age(age),
		 _gender(gender),
		 _name(name) 
	{}

	~Person();

public:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
	void set_age(const int &value);
	void set_gender(const char &value);
	void set_name(const std::string &value);
};


void Person::set_age(const int &value){
	this->_age = value;
}

void Person::set_gender(const char &value) {
	this->_gender = value;
}

void Person::set_name(const std::string &value) {
	this->_name = value;
}

Person::~Person()
{
	//
}



int main() {

	// 聲明一個Person類型的對象
	// 請注意:對象的概念在這裏開始出現,因爲Person是一個集合,一個類型,那麼類型的一個一個實際的例子,就是對象
	Person m_person(25, '男', "chengzhen");

	// 獲得年齡
	m_person.get_age();

	// 獲得性別
	m_person.get_gender();

	// 獲得姓名
	m_person.get_name();

	// 設置年齡
	m_person.set_age(30);

	// 設置性別
	m_person.set_gender('nv');

	// 設置姓名
	m_person.set_name("Charmain");

	std::cin.get();
}
但是,C++是支持這種行爲的。友元(友元函數、友元成員函數、友元類):

//----------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------
// 類的封裝
class Person{

public:
	Person(int age, char gender, std::string name)
		:_age(age),
		 _gender(gender),
		 _name(name) 
	{}

	~Person();

public:
	int  _age;
	char _gender;
	std::string _name;
public:
	const int get_age() { return _age; }
	const char get_gender() { return _gender; }
	const std::string get_name() { return _name; }
	friend void information(const Person &P);
};

void information(const Person &P){
	std::cout << "The age of Person is " << P._age << std::endl;
	std::cout << "The gender of Person is " << P._gender << std::endl;
	std::cout << "The name of Person is " << P._name << std::endl;
}

Person::~Person(){
	//
}



int main() {

	// 聲明一個Person類型的對象
	// 請注意:對象的概念在這裏開始出現,因爲Person是一個集合,一個類型,那麼類型的一個一個實際的例子,就是對象
	Person m_person(25, '男', "chengzhen");

	// 獲得年齡
	m_person._age;
	m_person.get_age();

	// 獲得性別
	m_person._gender;
	m_person.get_gender();

	// 獲得姓名
	m_person._name;
	m_person.get_name();

	std::cin.get();
}

上邊的代碼片段中,我們添加了一個函數,在類中被修飾成爲“friend”,意味着這個函數是個友元函數,並不是Person類的成員函數。我們在函數的實現中可以看見,infromation函數的前面並沒有形如“Person::”的作用域操作符,也意味着並不是Person類的成員函數,而是一種類中的特殊函數。但是我們可以在函數體中,通過參數聲明的對象,任意調用類的私有成員變量。乍一看,貌似這種行爲打破了“封裝性”。一句話,方便了使用,打破了封裝,可謂功過相抵。

未完,待續。














發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章