物件導向武功秘笈(2):招式篇— Python與Java的 ... - YC Note
文章推薦指數: 80 %
抽象化:抽象類別(Abstract Class)、抽象方法(Abstract Method)和接口(Interface). 事實上,剛剛使用 Animal 的方法並不是很正確,我們將 Animal 當作一個 ...
物件導向武功秘笈(2):招式篇—Python與Java的物件導向編程介紹
YCChen
2018-04-10
Coding
軟體設計
物件導向編程/類別(Class)與物件(Object)/方法多載(MethodOverloading)/物件導向三大特性—封裝(Encapsulation)/物件導向三大特性—繼承(Inheritance)/抽象化:抽象類別(AbstractClass)、抽象方法(AbstractMethod)和接口(Interface)/物件導向三大特性—多型(Polymorphism)/
物件導向編程
在上一章當中,我們藉由好的程式碼的特性:「正常執行」、「穩健」、「不重複撰寫」、「可讀性」、「可擴展」,自然而然引出物件導向的概念。
在這一章當中YC會接續介紹完整的物件導向要如何實現,包括物件導向三大特性:封裝、繼承和多型。
在本章我會採用兩種語言交叉作說明,一種是靜態型別的語言Java,另一種是動態型別的語言Python,這兩種語言都是可以實現物件導向的語言,而所謂型別的動態與靜態可以用一個簡單的方法來區分:型別檢查(TypeChecking)發生在什麼時候?像Java這類的靜態型別語言,它的型別檢查是在編譯時期(CompileTime)完成的,而像是Python這類的動態型別語言,它的型別檢查則是在執行時期(Runtime)才去做,所以Python可以不事先宣告變數型別,這點使得Python在開發上方便許多。
雖然Python和Java都是支援物件導向的語言,但在使用上有很大的差異,首先,因為Python的動態型別,所以有些物件導向的性質對它來說就不是那麼重要,另外,因為Python追求簡潔,簡化了相當多的東西,所以很多的使用方法不同於傳統的物件導向,需要認識到這些差異才可以讓你使用Python的物件導向不會顯得很彆扭。
Java是一套對物件導向支援非常完整的語言,而Python是一套易於快速開發的語言,使用兩種語言說明物件導向是為了讓讀者更能了解物件導向的本質,而非語言本身。
本篇採用『大話設計模式』書中的物件導向篇範例。
類別(Class)與物件(Object)
首先來看物件導向的基本組成,類別(Class)與物件(Object)。
類別:建立物件的藍圖,描述所建立的物件共同的屬性和方法。
物件:一個自我包含的實體,物件包括屬性(Properties)和方法(Methods),屬性就是需要記憶的資訊,方法就是物件能夠提供的服務。
舉個例子,我想要創造一隻有名字的貓,她有喵喵叫的能力,在Java中可以寫成
/*Java*/
classCat{//{1}
privateStringname;//{2}
publicCat(Stringname){//{3}
this.name=name;//{4}
}
publicStringshout(){//{5}
return"Mynameis"+name+".meow~";//{6}
}
}
publicclassTest{
publicstaticvoidmain(String[]args){//{7}
Catcat=newCat("May");//{8}
System.out.println(cat.shout());//{9}
}
}
//output:
//MynameisMay.meow~
{1}建構一個Cat的類別,類別不是物件,類別只是物件的藍圖。
{2}建立一個私有變數name,用來代表貓的名字,我們使用private的修飾詞讓它是私有的,也就是說外部環境沒辦法去讀取到這個變數,只有物件內部才可以讀取的到
{3}提供建造方法(constructor)來初始化這一個物件,初始化需要name的參數。
{4}在初始化的過程中,我們會將從外部讀取的name存入私有變數this.name裡,在Java裡頭,如果外部變數名稱與本地變數名稱相同,需要使用this來特別區分。
{5}創造一個公開的類別方法shout()。
{6}使用私有變數name讓貓可以自我介紹,再發出喵喵叫的聲音。
{7}Java只要遇到main就會去執行,方法main具有靜態方法的修飾詞static,也就是說Test不需要被實體化也能執行main這個方法。
{8}使用new來創造一個物件,在創造的過程會執行初始化,所以必須放入初始化需要的參數name,所以上面的新的物件有了"May"的名字。
{9}接下來使用cat.shout()去執行喵喵叫的動作,這個方法會回傳字串,再利用System.out.println的方法將字串顯示出來。
注意!在物件導向的習慣中,會用.來表示在那物件中的方法或屬性,所以cat.shout()就是執行在物件cat中的方法shout()。
再來看Python怎麼表示,
###Python3.4
classCat:#{1}
def__init__(self,name):#{2}
self.__name=name#{3}
defshout(self):#{4}
return"Mynameis"+self.__name+".meow~"#{5}
defmain():
cat=Cat("May")#{6}
print(cat.shout())#{7}
if__name__=="__main__":#{8}
main()
#output:
#MynameisMay.meow~
{1}建構Cat的類別,這是Python3的表示方法,如果是使用Python2.7的話,要寫成classCat(object):才可以。
{2}Python的初始化方法,Python在初始化之前會先自行執行__new__的方法,這個過程會產生一個新的物件,也就是實體化,而這個新的物件會以第一個參數的方法被帶入__init__的方法裡進行初始化,我們通常會命名這個變數為self,這裡的self已經是個物件而不是類別,那初始化的過程需要引入外部資訊name的參數來進行命名,所以第二個參數就要設name,記住喔!第一個參數是Python自動產生的,不是由外部帶入的,所以外部只要給name一個參數就足夠了。
{3}創造一個私有本地變數__name來將name存入,在Python當中以雙底線__開頭的變數會被視為是「私有的」,效果和Java的private接近,不過Python並沒有這麼嚴格禁止外部去讀取私有變數,所以需要配合工程師的自我規範。
{4}類別方法shout(),只要你不是靜態的類別方法,Python都會自動幫你帶入物件資訊當作第一個參數,通常命名為self,那為什麼靜態方法沒有自動帶入,因為靜態方法不用實體化,所以根本不擁有物件的資訊。
{5}使用到本地的self.__name變數
{6}創造一個物件,在創造的過程會執行初始化,所以必須放入初始化需要的參數name,所以上面的新的物件有了"May"的名字。
{7}接下來使用cat.shout()去執行喵喵叫的動作,這個方法會回傳字串,再利用print的方法將字串顯示出來。
注意!在物件導向的習慣中,會用.來表示在那物件中的方法或屬性,所以cat.shout()就是執行在物件cat中的方法shout()。
{8}在Python程式執行時,它的__name__會是"__main__",也就是說會去執行這個if判斷式下面的程式。
方法多載(MethodOverloading)
物件導向允許「使用不同的參數形式去實現同一個方法」,這就稱之為方法多載,這個方法涵蓋一般方法和構造初始化方法。
來延伸剛剛的貓的例子,假設今天我們允許用戶不去設定貓咪的名字,而程式會預先給貓咪No-Name的預設值,所以我們需要另外一個初始化方法是不用貓咪名字的參數形式。
Java的實現程式碼如下所示,如此一來只要碰到沒有參數的形式,程式會給予"No-Name"的名字去當作貓咪的名字,並進行初始化。
/*Java*/
classCat{
privateStringname;
publicCat(Stringname){
this.name=name;
}
//methodoverloading
publicCat(){
this("No-Name");//given"No-Name"asitsname
}
publicStringshout(){
return"Mynameis"+name+".meow~";
}
}
publicclassTest{
publicstaticvoidmain(String[]args){
Catcat=newCat();//no-argumantformat
System.out.println(cat.shout());
}
}
//output:
//MynameisNo-Name.meow~
但在Python當中,不允許這種「相同方法名稱,卻又不同參數形式」,Python採用其他的方式來產生同樣的方法多載效果,如以下所示,我們可以看到Python使用default方法來實現多載,只要我們不給予name,它的default就是"No-Name"。
###Python3.4
classCat:
def__init__(self,name="No-Name"):#name'sdefaultis"No-Name"
self.__name=name
defshout(self):
return"Mynameis"+self.__name+".meow~"
defmain():
cat=Cat()#no-argumentformat
print(cat.shout())
if__name__=="__main__":
main()
#output:
#MynameisNo-Name.meow~
物件導向三大特性—封裝(Encapsulation)
還記得「低耦合,高內聚」的原則嗎?為了符合這原則,每個物件都要盡可能的去包含需要用到的屬性和方法,並且使得外部不能以不合理的方法去影響物件,這就稱之為「封裝」。
我們來看看上次的成果,我們就用這個例子來說明「封裝」。
###Python3.4
importsys
classCalculation:
def__init__(self,nums):
self.__nums=nums#{1}
fornuminself.__nums:
self.__checkPositiveInteger(num)
def__checkPositiveInteger(self,num):#{2}
if(notisinstance(num,int))or(num<=0):
raiseValueError("invalidpositiveinteger:"+str(num))
def__primeFactorize(self,num):#{3}
prime_factorize=dict()
i=2
while(num>1):
ifnum%i==0:
prime_factorize[i]=prime_factorize.get(i,0)+1
num/=i
else:
i+=1
returnprime_factorize
deffindGCF(self):
prime_factorize=list()
fornuminself.__nums:
prime_factorize.append(self.__primeFactorize(num))
common_prime=set(prime_factorize[0].keys())
forpfinprime_factorize[1:]:
common_prime&=set(pf.keys())
gcf=1
forprimeincommon_prime:
m=sys.maxsize
forpfinprime_factorize:
m=min(m,pf[prime])
gcf=gcf*(prime**m)
returngcf
deffindLCM(self):
gcf=self.findGCF()
lcm=gcf
fornuminself.__nums:
lcm*=int(num/gcf)
returnlcm
{1}使用私有變數,讓外部不能任意的改變self.__name變數,在這個例子當中,如果self.__name被任意改變,它將會逃過__checkPositiveInteger的檢查。
{2}&{3}不需要由外部讀取的方法,就盡量讓它是私有的。
再回到貓咪的這個例子,如果我們想要可以調控「叫聲次數」的話,可以這樣實現。
/*Java*/
classCat{
privateStringname="";
publicCat(Stringname){
this.name=name;
}
publicCat(){
this("No-Name");
}
privateintshout_num=3;//{1}
publicintgetShoutNum(){returnshout_num;}//{2}
publicvoidsetShoutNum(intnum){//{3}
if(num<0)thrownewjava.lang.IllegalArgumentException();
shout_num=num;
}
publicStringshout(){
Stringresult="";
for(inti=0;i
延伸文章資訊
- 1[Python物件導向]Python多型(Polymorphism)實用教學
必須透過繼承(Inheritance)的類別來進行抽象方法(Abstract Method)的實作,如下範例:. from abc import ABC, abstractmethod ...
- 2【java】使用Python建立和實現介面(interface)? - 程式人生
我有兩(2)個問題:首先,如何使用Python建立FlyBehavior interface ?其次,如何使用Python對FlyWithWings類中的FlyBehavior介面進行 impl...
- 3Implementing an Interface in Python - Real Python
An informal Python interface is a class that defines methods that can be overridden, but there's ...
- 4[Python教學]物件導向-Class類的封裝/繼承/多型 - MAX行銷誌
在上一篇,我們解釋了Python 中一切皆為物件,和什麼是物件Object此篇將帶 ... Python 建立class,通常類名採用大寫(下面範例為Employee ),在類中 ...
- 5【Python 教學】OOP 繼承/封裝/多型基本用法Example - 測試先生
Python 繼承基本用法(inheritance) example. 如何使用繼承(inheritance)的寫法呢? DisplayNameAge為父類別(Base class),Enter...