Dùng OO trong VB.NET
Ngăn cản Thừa kế
Bình thường (By default) class nào cũng có thể được dùng làm base class để từ đó ta thừa kế. Nhưng đôi khi ta không muốn cho ai thừa kế từ một Class nào đó, để làm việc ấy ta dùng keyword NotInheritable khi declare class:
Public NotInheritable Class KhôngCon End Class |
Khi ta đã dùng keyword NotInheritable rồi thì không class nào có thể dùng keyword Inherits để tạo một subclass từ class ấy.
Khi ta dùng đặc tính thừa kế để tạo một SubClass thì class mới nầy có đủ mọi methods, propertỉes và variables với Access Modifier Public hay Friend của SuperClass. Bất cứ thứ gì declared là Private trong SuperClass thì SubClass không thấy hay dùng được.
Có một ngoại lệ là New method. Các Constructor methods cần
phải được implemented (định nghĩa)
lại trong mỗi SubClass. Một chốc nữa
ta sẽ bàn vào chi tiết về điểm nầy.
Để làm sáng tỏ vấn đề SubClass có thể dùng Class Members nào của SuperClass, ta thử code lại Function Amount trong LineItem class bằng cách khiến nó gọi một Private Function tên CalculateAmount để tính ra Amount thay vì để nó tính trực tiếp như trước đây:
Public Function Amount() As Single Return CalculateAmount End Function Private Function CalculateAmount() As Single Return mintQuantity * msngPrice End Function |
Khi ta SubClass LineItem để tạo ra
ServiceLine class, bất cứ Object ServiceLine nào cũng thừa kế Function
Amount vì Function ấy được declared Public trong BaseClass LineItem. Ngược
lại, vì Function CalculateAmount là Private nên cả ServiceLine class lẫn bất
cứ client code nào dùng một LineItem Object đều không truy cập nó
được.
Như thế, mặc dầu ta gọi Function Amount được, nhưng đến phiên
nó gọi Private Function CalculateAmount thì có bị trở ngại không? Không sao
cả. Vì Function Amount nằm trong cùng Class với Private Function
CalculateAmount nên nó có thể gọi được, dù rằng ta gọi Function Amount từ
ServiceLine hay client code.
Thí dụ trong client code ta có những
hàng code như sau:
Protected Sub BtnShowAmount_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles BtnShowAmount.Click
Dim Service As ServiceLine
Service = New ServiceLine()
Service.Item = "Delivery"
Service.Price = 50
Service.DateProvided = Now
MessageBox.Show (Service.Amount.ToString, "Amount", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
|
Kết quả sẽ được hiển thị trong
message box, cho thấy Function CalculateAmount được Function Amount gọi dùm
cho client code dù rằng cả client code lẫn ServiceLine code đều không thể
gọi trực tiếp được.
Điểm nầy nhắc tôi nhớ lại khi còn bé, có lần bà
con trong vườn đem ra chợ cho ba má tôi cả thúng xoài thơm rất ngon. Bạn tôi
ở lối xóm thấy vậy biểu tôi lén lấy hai trái xoài để ăn vụn. Vì không phải
là người nhà nên bạn tôi không thể lấy đuợc xoài, bởi Access Modifier của
thún xoài là Private trong nhà tôi. Nhưng vì tôi là Public, nên bạn tôi có
thể nhờ tôi lấy dùm.
Đôi khi Public hay
Private thôi chưa đủ. Nếu ta declare thứ gì Private thì nó hoàn toàn
giới hạn trong class, ngược lại nếu ta declare nó Public (hay Friend) thì nó
có thể được dùng trong subclasses hay client code.
Tuy nhiên, có lúc
ta muốn một class member chỉ có thể được dùng trong subclasses thôi, chớ
không cho client code dùng. Trong trường hợp ấy ta dùng keyword
Protected. Thí dụ:
Public Class FatherClass
Protected DiSản As Single
End Class
Public Class SonClass
Inherits FatherClass
Public Function ChiaCủa() As Single
Return Disản
End Function
End Class
|
Ở đây ta có BaseClass
FatherClass với Protected Field Disản. Không có client code
nào có thể thấy Field DiSản được. Thế nhưng bất cứ SubClass nào của
FatherClass cũng đều thừa kế và dùng được DiSản.
Trong thí dụ trên,
một lần nữa SubClass có một Public method (ChiaCủa) có thể return một
protected value - nhưng chính value ấy, DiSản, không trực tiếp cho phép
client code dùng.
Chúng ta biết rằng đặc
tính quan trọng của Inheritance là một SubClass chẳng những thừa kế
behaviours của ParentClass mà còn có thể override (lấn quyền) các
behaviours ấy nữa. Chúng ta đã thấy một SubClass có thể extend (thêm
ra) ParentClass bằng cách cho thêm các methods Public, Protected và Friend.
Hơn nữa, khi dùng overriding, một SubClass có thể alter (sửa đổi)
behaviours của các methods trong ParentClass.
Bình thường (By
default), ta không thể override methods trong ParentClass trừ khi các
methods ấy được declared với keyword Overridable trong ParentClass.
Thí dụ:
Public Class ClassCha
Public Overridable Sub ChàoHỏi()
MessageBox.Show("Chào các cháu", "Class Cha")
End Sub
End Class
|
Tiếp theo, khi tạo một SubClass, nếu
muốn ta có thể override behaviour của Sub ChàoHỏi bằng cách dùng
keyword Overrides như sau:
Public Class ClassCon
Inherits ClassCha
Public Overrides Sub ChàoHỏi()
MessageBox.Show("Thưa các Bác", "Class Con")
End Sub
End Class
|
Bây giờ ta có thể viết client code
như sau:
Private Sub BtnSubClassObject_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles BtnSubClassObject.Click
Dim obj As New ClassCon()
obj.ChàoHỏi()
End Sub
|
Khi ta click button BtnSubClassObject program sẽ hiển thị message dialog dưới đây:
Tuy nhiên, hãy xem trường hợp ta code như sau:
Private Sub BtnParentClassObject_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles BtnParentClassObject.Click
Dim obj As ClassCha
obj = New ClassCon()
obj.ChàoHỏi()
End Sub
|
Trước hết, ở đây có vẻ kỳ kỳ, tại sao
declare một variable loại ClassCha mà lại instantiate một object ClassCon.
Chuyện đó hoàn toàn bình thường, vì ClassCon là một ClassCha. Tức là
một variable loại ClassCha hay ClassCon đều có thể chứa, thật ra là hold
references to (point to, chỉ tới), một instance của
ClassCon.
Điểm nầy áp dụng tổng quát khi ta dùng Inheritance. Một
variable loại SuperClass có thể hold reference to bất cứ SubClass Object nào
thừa kế từ SuperClass ấy. Đó là một cách để ta implement tính đa dạng
(polymorphism).
Đều có thể làm ta ngạc nhiên là khi ta click button
BtnParentClassObject ta cũng thấy hiển thị message " Thưa các Bác".
Sao
lạ vậy? Variable obj được declared là ClassCha tại sao message không
phải là "Chào các cháu"? Lý do là Sub ChàoHỏi của
ClassCon được gọi thay vì Sub ChàoHỏi của ClassCha. Ta nói Sub
ChàoHỏi là Virtual method. Tất cả methods trong VB.NET đều là
virtual.
Ý niệm virtual để nói rằng cái implementation của con
cháu trẻ nhất trong dòng họ được dùng - không cần biết là variable có
data type là class của thế hệ nào trong dòng họ. Tức là, nếu variable
dùng trong client code hold references to ClassÔngNội, ClassCha, ClassCon
hay ClassCháu thì method trong ClassCháu được gọi. Nếu trong ClassCháu không
có implementation của method thì ta gọi method trong ClassCon, nếu không có
thì gọi method trong ClassCha .v.v.. theo thứ tự từ bề dưới lên bề trên.
Bạn có thể Download
source code của program nầy tại đây.
Keyword Me được dùng
khi ta muốn nói rõ (explicitly) rằng ta muốn dùng method của chính cái Class
đang chứa code ấy, chớ không phải một implementation nào khác của method
ấy.
Cũng có trường hợp ta phải dùng keyword Me để nói ta muốn dùng
class-level variable chớ không phải procedure-level variable
có cùng tên. Một procedure-level variable, tức là local variable của một
method, có cùng tên với một class-level variable được gọi là shadowed
variable. Thí dụ:
Public Class TheClass
Private strName As String
Public Sub DoSomething()
Dim strName As String
strName = "Quang"
End Sub
End Class
|
Ở đây, variable strName được
declared ở class-level và bên trong Sub DoSomething. Bên trong method
ấy local variables (kể cả shadowed variables) sẽ được dùng vì chúng che đậy
class-level variables trừ khi ta nói rõ rằng phải dùng variable của
class-level bằng cách dùng keyword Me:
Public Class TheClass
Private strName As String
Public Sub DoSomething()
Dim strName As String
strName = "Quang" ' thay đổi value của local (shadowed) variable
Me.strName = "Kim" ' thay đổi value của class-level variable
End Sub
End Class
|
Keyword Me rất tiện dụng
khi ta muốn dùng Class members của chính Class chứa code. Tương tự như vậy,
đôi khi ta muốn dùng Class method của BaseClass (cũng gọi là SuperClass),
chớ không phải một implementation của method ấy trong SubClass. Nhớ là một
virtual method luôn luôn gọi implementation của Class trẻ nhất.
Từ
trong một SubClass, nếu muốn gọi một method của BaseClass ta dùng keyword
MyBase như sau:
Public Class ClassCon
Inherits ClassCha
Public Overrides Sub ChàoHỏi()
MessageBox.Show("Thưa các Bác", "Class Con")
MyBase.ChàoHỏi()
End Sub
End Class
|
Bây giờ nếu ta chạy Sub ChàoHỏi của
ClassCon ta sẽ có hai messages, một cái từ ClassCon theo sau bởi một cái từ
ClassCha.
MyBase chỉ nói đến BaseClass trực tiếp, tức là Class cha
thôi chớ không nói đến Class ông nội. Không có cách nào để nói đến hơn một
thế hệ.
Dầu vậy, keyword Mybase có thể được dùng cho bất cứ thứ gì đã
được declared Public, Friend hay Protected trong ParentClass. Điều nầy kể
luôn cả những thứ mà ParentClass thừa kế từ các thế hệ trước trong gia đình,
tức là ClassÔngNội, ClassÔngCố .v.v..
Vì lý do virtual method,
ta sẽ gặp những trường hợp rắc rối như khi code của ParentClass lại chạy
code của SubClasses.
Khi viết code của một class, từ method nầy ta
thường gọi những methods khác nằm trong cùng class. Thí dụ như:
Public Class ClassCha
Public Sub VôĐề()
ChàoHỏi()
End Sub |
Trong trường hợp nầy, VôĐề gọi
Sub ChàoHỏi để đón tiếp. Để ý là vì ChàoHỏi được declared Overridable
nên rất có thể một SubClass sẽ implement method ChàoHỏi và lấn quyền nó. Thí
dụ:
Public Class ClassCon
Inherits ClassCha
Public Overrides Sub ChàoHỏi()
MessageBox.Show("Thưa các Bác", "Class Con")
End Sub
End Class
|
Vì đặc tính virtual của ChàoHỏi nên
ta tưởng ClassCha execute chính Sub ChàoHỏi của nó nhưng té ra nó lại
execute code của ChàoHỏi trong ClassCon. Trong code dưới đây, một Object
ClassCon gọi Sub VôĐề của ClassCha:
Private Sub BtnSubClassObject_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles BtnSubClassObject.Click
Dim obj As New ClassCon()
obj.VôĐề()
End Sub
|
Trong ClassCha, Sub VôĐề gọi ChàoHỏi
của chính nó, tuy nhiên Sub ChàoHỏi ấy bị overridden bởi implementation của
ChàoHỏi trong ClassCon. Do đó, program sẽ hiển thị message "Thưa các
Bác".
Nếu ta không muốn như vậy, ta muốn VôĐề execute chính code
của ChàoHỏi trong ClassCha thì phải dùng keyword MyClass như
sau:
Public Class ClassCha
Public Sub VôĐề()
MyClass.ChàoHỏi()
End Sub |
Ở đây ta không thể dùng keyword Me vì VôĐề có gọi ChàoHỏi ở class-level trong ClassCha chớ không phải trong một SubClass, nhưng bị overridden. Hình dưới đây minh họa quá trình gọi VôĐề từ client code:
Sub VôĐề thật ra nằm trong ClassCha mà ClassCon thừa kế nên VôĐề được executed trong ClassCha và gọi Sub ChàoHỏi trong cùng class ( ClassCha). Nhưng vì ClassCon có một implementation của Sub ChàoHỏi nên nó overrides ChàoHỏi của ClassCha.
Chúng ta đã thấy ta
có thể override methods và dùng các keywords Me, MyBase và MyClass để gọi
các overriden methods trong dây chuyền thừa kế. Tuy nhiên, đối với
Constructor của class thì có những luật lệ đặc biệt dành riêng cho method
New.
Những methods New không tự động di truyền từ BaseClass
xuống SubClass. Mỗi SubClass phải có một implementation riêng cho
Constructor dù rằng, nếu muốn, nó có thể gọi vào BaseClass với keyword
MyBase:
Public Class ClassCon
Inherits ClassCha
Public Sub New()
MyBase.New()
' để thêm các code khác để initialise tại đây
End Sub
End Class
|
Khi gọi Constructor của BaseClass, ta
phải gọi nó trước nhất - nếu không sẽ bị error. Tuy nhiên ta không cần gọi
Constructor của BaseClass vì Constructor của BaseClass được gọi tự
động.
Có một luật đặc biệt là nếu tất cả methods New trong BaseClass
đều đòi hỏi parameters thì ta phải implement ít nhất một method New trong
SubClass và ta phải đặt statement MyBase.New ngay phía đầu.
Dĩ
nhiên là ta có thể Overload method New trong SubClass, nhưng ta phải tự lo
liệu cách gọi một method New thích hợp trong BaseClass.
Cho đến giờ ta đã bàn về virtual method với đặc tính override trong nguyên tắc thừa kế. Trong các thí dụ trước đây BaseClass được instantiated thành Object để làm chuyện nầy, chuyện kia. Nhưng đôi khi ta muốn tạo một BaseClass chỉ để dùng cho thừa kế mà thôi.
Trở lại cái thí dụ về Inheritance với Class LineItem. Sở dĩ ta đặt ra Class LineItem là vì nó chứa những thứ chung cho cả hai classes ProductLine và ServiceLine. Chớ thật ra một Object của Class LineItem không chứa đủ mọi đặc tính để làm một việc gì thực tế. Nếu ta muốn nói rõ rằng Class LineItem chỉ được dùng để tạo những SubClasses bằng cách thừa kế từ nó, ta có thể declare như sau:
Public MustInherit Class LineItem |
Tức là ta chỉ thêm keyword
MustInherit thôi, chớ không thay đổi gì khác. Kết quả là từ nay
Client code không thể instantiate một Object từ Class LineItem. Do đó dòng
code sau sẽ bị syntax error:
Dim myObject As New LineItem() |
Thay vào đó, nếu muốn dùng LineItem ta phải tạo SubClass từ nó.
Tương tự với ý niệm Phải-được-thừa-kế trong Class, ta cũng có MustOverride cho một method. Có thể trong BaseClass ta khai báo một method, nhưng ta đòi hỏi method ấy phải có một implementation trong SubClass. Ta declare như sau:
Dim MustOverride Sub CalculatePrice |
Để ý là ở đây không có thân thể của Sub CalculatePrice hay statement End Sub gì cả. Khi dùng MustOverride ta không được phép cung cấp một implementation cho method trong BaseClass. Một method như thế được gọi là abstract method hay pure virtual function, vì nó chỉ có phần khai báo chớ không có phần định nghĩa. Những abstract methods phải được overridden trong bất cứ SubClass nào của BaseClass thì mới dùng được. Nếu không, ta sẽ không có phần implementation của method đâu cả và khi compile sẽ gặp syntax error.
Nếu hợp cả hai ý niệm MustInherit và MustOverride lại ta sẽ tạo ra một abstract base class. Đây là một Class chỉ có khai báo chớ hoàn toàn không có implementation. Ta phải SubClass từ nó thì mới làm việc được, thí dụ như:
Public MustInherit Class ClassCha Public MustOverride Sub VôĐề() Public MustOverride Sub ChàoHỏi() End Class |
Kỹ thuật nầy rất thích hợp để ta code
cái sườn hay bố cục của program ngay trong lúc thiết kế. Class nào thừa kế
ClassCha thì phải implement cả Sub VôĐề lẫn Sub
ChàoHỏi, nếu không sẽ bị syntax error.
Nhìn về một phương diện,
abstract base class rất giống khai báo Interface. Nếu dùng Interface, chúng
ta có thể khai báo như sau:
Public Interface ICha Sub VôĐề() Sub ChàoHỏi() End Interface |
Bất cứ class nào chịu implement
interface ICha thì phải implement cả Sub VôĐề lẫn Sub
ChàoHỏi, nếu không sẽ bị syntax error - do đó, ta thấy Interface rất
giống một abstract base class.
Sự khác biệt chính giữa abstract base
class với Interface là ở chỗ thừa kế. Khi ta tạo một class con bằng cách
SubClass từ ClassCha, chính class con ấy lại cũng có thể được SubClassed.
Mấy class cháu nầy sẽ tự động thừa kế VôĐề và ChàoHỏi từ class
con.
Trong khi ấy nói về Interface, mỗi class phải tự implement ICha
một cách độc lập và phải cung cấp hai Subs VôĐề và ChàoHỏi của chính nó. Vì
thế, nếu ta không có ý định dùng lại code của các Subs khi ta tạo các
classes mới thì ta có thể dùng interface. Ngược lại nếu ta muốn dùng lại
code trong SubClass theo nguyên tắc thừa kế thì ta nên dùng abstract base
class.
|
Vovisoft © 2000. All rights reserved. | ||||
|
Last Updated: 15 th3 2002 |
||||
PcLeHoan
1996 - 2002
Mirror :
http://www.pclehoan.com
Mirror :
http://www.lehoanpc.net
Mirror :
http://www.ktlehoan.com