Remove ads
一種軟件開發方法,用密切相關嘅數據同方法作為對象。 From Wikipedia, the free encyclopedia
物件導向編程(英文:OOP,個簡稱可作粵拼:ou1 ou1 pi1)係程式編寫範式一種。如果話一位軟件工程師用 OOP 嚟寫程式,即係話佢以物件嚟做個程式嘅基本單元:一件物件會包括一柞特定嘅數據,呢啲數據(特性)因為用途上有某啲共通點而俾設計者擺埋一齊嚟睇;除此之外,一件物件通常仲會掕住一啲專門處理同嗰件物件相關嘅工作嘅子程序,呢啲子程序就係嗰件物件嘅方法[1][2]。
舉個例說明,想像有隻電子遊戲嘅程式,當中有兩件數據-燃料嘅量同埋最大速率,兩件數據用途上有共通點,都係描述緊一架車嘅特性,所以設計者就教個程式將兩件數據擺埋一齊做件物件噉睇;而且件物件仲掕住咗柞方法,例如 refuel()
呢個子程序就係用嚟補充燃料(即係將燃料量變返做佢最大可能數值)嘅。部電腦內部會記住「呢幾個特性同方法屬同一件物件」,並且喺用家介面嗰度將佢哋擺埋一齊展示,等用家做起程式編寫上嚟易搞好多[3]。
喺廿一世紀初,有好多重要嘅應用-包括遊戲編程[4]同人工智能[5]呀噉-都成日會用到 OOP。因為 OOP 用途咁廣泛,有啲程式語言甚至仲係專門設計到支援 OOP 嘅,包括咗廿一世紀初常用嘅 C++、C♯、Java、Python 同 MATLAB 呀噉-呢啲程式語言就係所謂嘅物件導向程式語言。
OOP 係一種程式編寫範式,根基概念係類別[e 1]同物件[e 2]。喺最基本嘅 OOP 當中,個程式裏面啲物件多數都係有返啲範本嚟參照造出嘅,呢啲範本就係所謂嘅類別-一個類別會講明屬於呢個類別嘅物件每一件會掕住嘅
而一件物件就係佢所屬嗰個類別嘅一個實例[e 5],即係按個類別嘅模嚟造出嘅[6]。用以下嘅 MATLAB 碼做例子[7][8]:
classdef BasicClass % 定義 BasicClass 呢個 class...
properties % 呢個 class 有以下呢啲特性...
Value {mustBeNumeric} % Value 係唯一一個特性,一定要係一個數...
end
methods % 呢個 class 有以下呢啲方法...
function r = roundOff(obj) % 每個 function 都係一個方法...
r = round([obj.Value],2);
end
function r = multiplyBy(obj,n)
r = [obj.Value] * n;
end
end
end
...
a = BasicClass; % 創造一件叫 a 嘅物件,件物件屬 BasicClass 呢個 class。
a.Value = pi/3; % a 嘅 Value 設做 pi/3(圓周率除 3)咁多。
OOP 嘅諗頭源自 1960 年代[9],到咗 1990 年代經已成為咗其中一種最重要嘅程式編寫範式。噉係因為事實表明喺好多應用上,類別同物件嘅做法都幫到手令個程式更加容易打理:舉例說明,想像有位遊戲製作師喺度編寫佢隻新遊戲嘅程式,佢想創造一個虛擬世界俾玩家喺裏面郁動;佢想個虛擬世界設計成好似一個現實嘅城市噉,度緊個城市其中一條街要點設計,佢想條街有幾架車泊咗喺度做佈景,佢可以設定一個「車」嘅類別,個類別每個實例都有「色水」同「位置」等嘅變數,然後叫個程式將「建構一個車嘅實例」呢樣嘢做三次-就唔使吓吓都重新打過「建構一個車嘅實例」嘅碼。有研究者指,依種程式寫法能夠令個程式嘅運作更加貼近現實世界嘅思維,而且彼此相關嘅資料同行為,都會歸一喺一件物件之內,會比較易管理[10][11]。
OOP 能夠達致抽象化:抽象化廣義上係指「忽略一個概念裏面啲唔重要嘅細節,淨係將啲重要嘅部份俾用家睇」;舉個例說明,想像一位遊戲製作師想寫一個遊戲程式,佢想喺隻遊戲入面俾玩家靠撳掣嚟控制主角郁(呢樣係好多遊戲都會用到嘅功能),於是(簡化講)佢就下載咗個程式[註 2],個程式能夠做到攞「玩家撳咗嘅掣」做 input
,並且俾出「令遊戲嘅主角郁」做 output
,而跟住呢位遊戲製作師就唔理個程式係點運作嘅,當個程式係個黑盒噉攞嚟用,淨係在意個程式嘅 input
同 output
關係(重要嘅部份)達得到佢想要嘅效果-即係將個程式內部啲演算法忽略咗[12]。
OOP 當中嘅「物件」概念都係做到好似抽象化噉嘅效果:用家有咗件物件就可以忽略「件物件係點運作嘅」呢條問題,淨係集中諗件物件嘅 input-output
關係,以及係呢件物件做到嘅 input-output
關係可以點樣幫佢達到佢想要嘅功能,幫用家慳返好多時間同精神[13]。
OOP 有齊嗮基本編程會用嘅功能,例如係變數、數據結構同埋子程序呀噉。除咗呢啲基本功能之外,行 OOP 嘅程式語言仲成日會有以下呢啲功能[14][15]:
物件生命週期[e 6]係指一件物件由創造至剷走之間嘅嗰段時間。當一個程式創建一件物件嗰陣,部電腦內部會搵個記憶體位置嚟裝住件物件嘅數據-「數據」包括件物件內部啲變數呀噉,而呢個時間點就係件物件嘅生命週期嘅起始點;喺個程式行嘅期間,個程式叫親部電腦用件物件,部機都會由件物件屬嘅記憶體位置攞數據用;而件物件相應嗰啲數據通常會喺以下呢啲情況下被剷走[16]:
release
),噉通常表示用有新嘅數據冚咗舊嗰啲佢;class Complex { // 定義 Complex 呢個類別...
... // 拉雜碼...
// 定義個建構子...
public Complex()
{
real = 0;
img = 0;
}
... // 拉雜碼...
~Complex() // 定義 Complex 呢個類別嘅解構子,個解構子執行嗰陣會令電腦彈「Call zo destructor」嘅字眼出嚟(方便編程員睇)。
{
Console.WriteLine("Call zo destructor");
}
}
封裝[e 8]係 OOP 當中嘅一個概念,負責將(通常係將屬同一件物件嘅)數據同埋操縱呢啲數據嘅子程序「包埋一齊」,等呢啲數據同子程序難以俾外界干擾同亂咁用。想像一個類別唔容許外界嘅碼直接使用一件物件內部嘅數據,淨係俾佢哋透過啲方法嚟使用啲數據,用返上面虛擬車嘅例子即係例如唔俾任何外界嘅碼直接更改 fuel
嘅數值,而淨係俾佢哋用 refuel()
呢個方法嚟改變 fuel
嘅數值,噉呢個類別就算係做咗封裝[19]。
例如係好似以下呢段 C♯ 碼噉,就表示緊一個封裝好嘅類別:
public class SaamLeonCe // 一個叫 Saam leon ce(三輪車)嘅 class...
{
protected void Pedal() { } // Pedal() 呢個方法淨係可以由屬同一個 class、或者由呢個 class 延伸出嚟嘅 subclass 嘅碼使用。
private int luk = 3; // luk 表示轆嘅數量,個特性係私有嘅,淨係可以由屬同一個 class 或者 struct 嘅碼使用。
...
}
封裝相關嘅功能喺 OOP 上有好多優點:封裝可以防止個程式嘅第啲部份修改個類別嘅數據,而喺做代碼重構[e 9]嗰陣可以令出錯嘅機會減低(因為保證咗個類別以外永遠都唔會有碼嘗試直接改變個類別嘅內容);而且如果個程式係預咗俾一啲外行嘅用家使用嘅話,將啲特性變成 private
又可以將啲數值收埋唔顯示喺用家介面嗰度,某程度上令到個程式易用啲-用家唔使睇住咁多唔同嘅變數[20]。
繼承[e 10]係 OOP 上嘅一個功能。喺 OOP 當中,繼承係指物件嘅類別之間可以有嘅一種關係。抽象啲噉講,如果話物件類別 A 由類別 B 嗰度「繼承」啲嘢,意思係指類別 B(超類別[e 11])有嘅特性,類別 A(子類別[e 12])冚唪唥有齊嗮,不過類別 A 可以有類別 B 冇嘅特性。噉做用意通常係因為類別 A 所表示嘅事物概念上係類別 B 所表示嘅事物嘅一種,而用繼承嘅做法可以令到寫程式嗰陣慳返好多時間精神[21]。
舉個例說明,想像家陣有個遊戲程式,個程式要展示一個虛擬世界,個世界入面
Animal
(動物)呢個類別,呢個類別具有一柞特性-data1
、data2
,亦有柞特定嘅方法,例如係包含教個程式隻虛擬動物要點樣郁動嘅子程序 move()
;Dog
(狗)呢個子類別,呢個類別由 Animal
呢個超類別嗰度繼承咗 move()
呢個方法-即係話無論係虛擬狗定第啲虛擬動物,個程式都會用同一個子程序嚟計算隻動物要點樣郁動;
Dog
又有 bark()
(吠)呢個佢獨有嘅方法,呢個方法係淨係屬 Dog
嘅物件先至會用到嘅。用繼承嘅做法會方便,因為個設計者唔使種種動物都同佢哋設一個類別同埋重新打過嗮啲好似 move()
噉種種動物都要用嘅碼[21][23]。例如係好似以下呢段簡化嘅 C♯ 碼噉:
class Dog : Animal { // 創造一個 class 叫 Dog,呢個 class 嘅 superclass 係 Animal。
// Dog 要做嘅嘢...
public Dog()
{
Console.WriteLine("Dog");
}
}
有啲程式語言(比較出名嘅有 C++ 同 Python)仲會支援多重繼承[e 14]嘅功能,即係俾一個子類別有多過一個超類別,而嗰啲超類別之間冇直接嘅繼承關係[24]。
喺 OOP 當中,物件組成[e 15]廣義上係泛指將啲物件或者資料類型砌埋一齊組成更加複雜嘅物件[25]。舉例說明,想像家陣有個人想編寫一個資訊系統程式,用嚟記住一間企業啲員工嘅個人資料,佢個程式可能會一段好似以下噉嘅 Python 碼[26]:
class JyunGung:
def __init__(self, id, name):
self.id = id
self.name = name
self.deizi = None
上述段碼做嘅係定義 JyunGung
(員工)呢個類別,個類別由幾個特性、方法同物件組成,當中 deizi
可以係一件物件,又會有 gaaimeng
(街名)同 city
(城市)等嘅組成部份[26]。
再進階啲嘅應用當中仲有所謂嘅物件結合[e 16]:物件結合意思係指件複雜物件 com
由多件物件組成,但 com
並唔「擁有」啲組成部份-當 com
俾人剷走嗰陣,如果用嘅係物件組成,組成 com
啲物件都會跟住被剷走,但喺物件結合之下,啲組成部份並唔隸屬於 com
,就算 com
俾人剷走,啲組成部份仲可以繼續噉存在。事實係有好多行 OOP 嘅程式語言都有陳述式專門做物件結合[25],例如想像一間大學又想整資訊系統記住啲員工嘅個資,可能會寫好似以下噉嘅 C++ 碼:
class GaauSau; // 假設 GaauSau(教授)呢個類別喺個程式嘅第度定義咗,為咗慳篇幅呢度唔詳講。
class BouMun { // 定義 BouMun(部門)呢個類別...
public:
BouMun(const std::string& title): title_(title) {}
private: // 物件結合:GaauSau 組成(aggregate) BouMun 呢個類別。
std::vector<std::weak_ptr<Professor>> members_;
const std::string title_;
};
-就算一個部門執咗,大學可能仲會想留住班教授同佢哋啲個資,調佢哋去第個部門做嘢,所以特登將個程式寫到唔會有「一剷咗一個部門嘅資料,啲教授嘅個資就跟住剷埋」噉嘅情況。
基於類別[e 17]定基於原型[e 18]係兩種相對嘅 OOP 做法[27]。基於類別編程意思係指個 OOP 程式以類別做出發點:
定義類別 X 有邊啲特性同方法; 建立物件 A,A 屬類別 X; 建立物件 B,B 屬類別 X... 如此類推;
而基於原型編程就唔同,唔會明確噉定義「個類別係乜」,而係會攞一嚿已有嘅物體做「原型」-
攞一嚿物件 A; 建立一件新嘅物件 B,並且指明 B 繼承嗮 A 啲特性同方法(A 係 B 嘅原型); 跟住可以用同樣嘅方法建立更多嘅物件;
喺廿一世紀初,主流嘅 OOP 係跟基於類別嘅做法嘅,不過都有唔少編程工作者比較鍾意用基於原型嘅做法,例如有啲工作者指出,基於類別嘅做法通常都係會將個程式啲類別定死咗,唔俾部電腦喺行個程式期間郁個類別嘅內容,而相比之下,基於原型嘅做法本質上就會容許呢樣嘢,好似係[28]:
攞物件 A 做物件 B 嘅原型; 改物件 B 嘅特性同方法; 攞物件 B 做物件 C 嘅原型;
而呢樣嘢被指能夠達致一啲基於類別做唔到嘅功能[29]。至於用基於原型編程嘅程式語言,可以睇吓 JavaScript [27]。
物件導向設計[e 19]指攞一個軟件設計上撞到嘅問題,諗計度出一個由物件組成嘅系統,用嚟解決個問題,不過仲未郁手寫源碼。舉個例說明,想像家陣有位遊戲製作師想要郁手開發一隻新嘅射擊遊戲,簡化噉講佢隻遊戲要呈現一個虛擬世界俾玩家睇,玩家要能夠喺個世界入面郁動同埋射擊虛擬嘅敵人;噉即係表示,隻遊戲嘅虛擬世界(一個程式)最低限度要有以下呢啲物件[30][31]-
... 呀噉。喺郁手寫源碼打前,位遊戲製作師度好嗮「要有咩類別」、「要有咩物件」、「每個類別要有咩特性同方法」[32]... 呢啲咁多問題嘅過程就係軟件設計,而好似上述個例子噉喺諗計階段經已打算用物件導向編程,就係所謂嘅物件導向設計[33]。
物件導向編程成日會配合以下呢啲設計模式[e 20][33][34]:
getInstance
噉嘅程序,用嚟確保個實例唔會喺個程式入面出現多過一次[35]:
public static synchronized Singleton getInstance() // synchronized 確保唔可以有兩段或者以上嘅過程同時行呢段碼;
{
if (obj==null) // 如果冇 instance...
obj = new Singleton(); // 就整個新 instance
return obj; // 將個新 instance 俾做 output
}
using namespace std;
enum VehicleType { // 列出 Vehicle 可以有邊幾種。
VT_TwoWheeler, VT_ThreeWheeler, VT_FourWheeler
};
class Vehicle {
public:
virtual void printVehicle() = 0;
static Vehicle* Create(VehicleType type);
};
// ... 每種 Vehicle 都有個類別。即係 class TwoWheeler : public Vehicle、class ThreeWheeler : public Vehicle 同埋 FourWheeler : public Vehicle。
Vehicle* Vehicle::Create(VehicleType type) { // 呢個係工廠方法-按 input 決定要建立邊個類別嘅物件;如果想加新類別,設計者淨係要改呢度。
if (type == VT_TwoWheeler)
return new TwoWheeler();
else if (type == VT_ThreeWheeler)
return new ThreeWheeler();
else if (type == VT_FourWheeler)
return new FourWheeler();
else return NULL;
}
class Client { // 有可能會想用 Vehicle 物件嘅類別
public:
Client()
{
VehicleType type = VT_ThreeWheeler;
pVehicle = Vehicle::Create(type); // Client 唔會親自建立物件,而係叫件工廠物件建立。
}
// 省略...
};
// 下略...
Subject
)內部留意住佢啲觀察者[e 25];每當某啲狀態出現變化嗰陣,Subject
都會通知自己啲觀察者,而且通常係透過叫觀察者嘅方法嚟通知[38][39];好似以下呢段 Java 碼噉:
// 省略開頭嗰啲 import...
class EventSource {
public interface Observer {
void update(String event);
}
private final List<Observer> observers = new ArrayList<>(); // EventSource 有張 list 記住自己啲 observer...
private void notifyObservers(String event) {
observers.forEach(observer -> observer.update(event)); // 用 observer.update 嚟通知 observer 發生咗咩事。
}
public void addObserver(Observer observer) {
observers.add(observer);
}
public void scanSystemIn() {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
notifyObservers(line);
}
}
}
... 呀噉。
因為 OOP 咁好使好用,有好多廿一世紀初常見嘅程式語言都支援 OOP(物件導向程式語言[註 5])。一般認為,Simula(1967 年)係史上第一隻用 OOP 嘅程式語言[41];除咗 Simula 之外,OOP 仲有好多語言可以用:
int a = 5;
System.out.print(a);
... 等等。
英文文獻:
外語詞彙:
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.