Programowanie obiektowe w VB cz 2

Transkrypt

Programowanie obiektowe w VB cz 2
Programowanie obiektowe w VB cz 2
Interfejsy
Interfejsy są listą metod, właściwości, zdarzeń i indeksowników. Jeśli jakaś klasa implementuje jakiś
interfejs, znaczy to, że użytkownik tej klasy może skorzystać ze wszystkich możliwości, jakie
zdefiniowane zostały w interfejsie.
Używanie interfejsów jest alternatywą do używania mechnizmu dziedziczenia i klas abstrakcyjnych
(ze słowem kluczowym MustInherit).
Pod względem syntaktyki interfejsy niewiele różnią się od klas abstrakcyjnych (z wszystkimi
metodami abstrakcyjnymi). Różnica głównie polega na tym, że klasy abstrakcyjne stanowią korzeń w
drzewie jednokrotnego dziedziczenia, w który pojawiają się klasy udostępniające implementacje
metod abstrakcyjnym. W przypadku interfejsów dziedziczenie może być wielokrotne (mówi się tu o
rozszerzaniu interfejsów). Ten sam interfejs może być ponadto implementowany przez klasy z
różnych drzew dziedziczenia, co więcej, ilość implementowanych interfejsów przez klasy nie jest
ograniczona do jednego (tzn. ilość implementowanych przez daną klasę interfejsów może być
arbitralna).
Podczas specyfikowania interfejsów łatwo zapomnieć kto jest za co odpowiedzialny. Dlatego zawsze
należy pamiętać, że:
Interfejs jest swego rodzaju kontraktem. Nazwy interfejsów przyjęłó się zaczynać dużą literą I
– stąd np. interfejs IPokazujący mógłby być interfejsem zawierającym metodę Pokaż.
Klasa implementująca interfejs jest klasą, która zgadza się wypełnić kontrakt zdefiniowany w
interfejsie. Np. klasa implementująca interfejs IPokazujący powinna dostarczyć implementację
metody Pokaż.
Klasa klienta jest klasą, która wywołuje metodę interfejsu dostarczoną przez klasę implementującą.
Np. klasa Edytor może wywoływać metodę Pokaż klasy Analizator, która implementuje interfejs
pokazujący.
Interfejsy są szeroko używane w platformie .NET. Wystarczy przyjrzeć się klasom kolekcji (array lists,
stacks, and queues)
Definicja interfejsu
Składnia definicji interfejsu jest następująca:
[ <attrlist> ] [ Public | Private | Protected | Friend | Protected Friend ] _
[ Shadows ] Interface name
[ Inherits interfacename[, interfacename ]]
[ [ Default ] Property proname ]
[ Function memberame ]
[ Sub memberame ]
[ Event memberame ]
End Interface
Większość z elementów występujących w powyższym wyrażeniu znana jest z definicji klasy. Z tym,
że teraz elementy wewnątrz interfejsu nie mają implementacji (są tu tylko sygnatury metod i zdarzeń)
ani modyfikatora dostępu (wszystkie metody interfejsu domyślnie są Public).
Przykład interfejsu, który definiuje metody Read, Write oraz właściwośc Status:
Interface IStorable
Sub Read()
Sub Write(object)
Property Status() As Integer
End Interface
Implementacja interfejsu
Przypuśćmy, że chcemy mieć klasę Document, której obiekty mogłyby być zapisywane w bazie
danych. Przypuśćmy, że klasa ta jest już zaimplementowana, a chcielibyśmy do niej dodać metody
zapisu i odczytu do i z bazy danych. Można to zrobić, deklarując klasę Document jako klasę
implementującą interfejs IStorable.
Aby zaimplementować interfejs IStorable należy:
1. Zadeklarować klasę Document jako klasę implementująca interfejs IStorable jak następuje:
Public Class Document
Implements IStorable
Można też alternatywnie napisać:
Public Class Document : Implements IStor
2. Zaimplementować wszystkie metody interfejsu (oraz zdarzenia, właściwości, itd. jeśli interfejs
zawiera ich deklaracje). Dla klasy Document i metody Read wyglądałoby to tak:
Public Sub Read() Implements IStorable.Read
Console.WriteLine("Implementing the Read Method for IStorable")
End Sub 'Read
W ogólności definicja klasy Document implementującej interfejs IStorable mogłaby wyglądać tak:
Public Class Document : Implements IStorable
Public Sub Read() Implements IStorable.Read
'...
End Sub 'Read
Public Sub Write(ByVal o As Object) Implements IStorable.Write
'...
End Sub 'Write
Public Property Status() As Integer Implements IStorable.Status
'...
End Property
End Class 'Document
Przykład pełnej implementacji:
Option Strict On
Imports System
Namespace InterfaceDemo
' define the interface
Interface IStorable
Sub Read()
Sub Write(ByVal obj As Object)
Property Status() As Integer
End Interface 'IStorable
' create a class which implements the IStorable interface
Public Class Document
Implements IStorable
Public Sub New(ByVal s As String)
Console.WriteLine("Creating document with: {0}", s)
End Sub 'New
' implement the Read method
Public Sub Read() Implements IStorable.Read
Console.WriteLine("Implementing the Read Method for IStorable")
End Sub 'Read
' implement the Write method
Public Sub Write(ByVal o As Object) Implements IStorable.Write
Console.WriteLine( _
"Implementing the Write Method for IStorable")
End Sub 'Write
' implement the property
Public Property Status() As Integer Implements IStorable.Status
Get
Return myStatus
End Get
Set(ByVal Value As Integer)
myStatus = Value
End Set
End Property
' store the value for the property
Private myStatus As Integer = 0
End Class 'Document
Class Tester
Public Sub Run()
Dim doc As New Document("Test Document")
doc.Status = -1
doc.Read()
Console.WriteLine("Document Status: {0}", doc.Status)
End Sub 'Run
Public Shared Sub Main()
Dim t As New Tester
t.Run()
End Sub 'Main
End Class 'Tester
End Namespace 'InterfaceDemo
Wynik działania programu:
Creating document with: Test Document
Implementing the Read Method for IStorable
Document Status: -1
A oto przykład implementacji dwóch interfejsów w jednej klasie:
Option Strict On
Imports System
Namespace InterfaceDemo
Interface IStorable
Sub Read()
Sub Write(ByVal obj As Object)
Property Status() As Integer
End Interface 'IStorable
' here's the new interface
Interface ICompressible
Sub Compress()
Sub Decompress()
End Interface 'ICompressible
' Document implements both interfaces
Public Class Document
Implements ICompressible, IStorable
' the document constructor
Public Sub New(ByVal s As String)
Console.WriteLine("Creating document with: {0}", s)
End Sub 'New
' implement IStorable
Public Sub Read() Implements IStorable.Read
Console.WriteLine("Implementing the Read Method for IStorable")
End Sub 'Read
Public Sub Write(ByVal o As Object) Implements IStorable.Write
Console.WriteLine( _
"Implementing the Write Method for IStorable")
End Sub 'Write
Public Property Status() As Integer Implements IStorable.Status
Get
Return myStatus
End Get
Set(ByVal Value As Integer)
myStatus = Value
End Set
End Property
' implement ICompressible
Public Sub Compress() Implements ICompressible.Compress
Console.WriteLine("Implementing Compress")
End Sub 'Compress
Public Sub Decompress() Implements ICompressible.Decompress
Console.WriteLine("Implementing Decompress")
End Sub 'Decompress
' hold the data for IStorable's Status property
Private myStatus As Integer = 0
End Class 'Document
Class Tester
Public Sub Run()
Dim doc As New Document("Test Document")
doc.Status = -1
doc.Read()
doc.Compress()
Console.WriteLine("Document Status: {0}", doc.Status)
End Sub 'Run
Shared Sub Main()
Dim t As New Tester
t.Run()
End Sub 'Main
End Class 'Tester
End Namespace 'InterfaceDemo
Wynik działania programu:
Creating document with: Test Document
Implementing the Read Method for IStorable
Implementing Compress
Document Status: -1
Rzutowanie do interfejsu
Interfejsy można używać podobnie jak używa się klas abstrakcyjnych w deklaracjach zmiennych
referencyjnych. Można na przykład zdefiniować zmienna typu interfejs, aby w niej przechować referencję do
obiektu klasy, która ten interfejs implementuje.
Dim doc As New Document()
Dim isDoc As IStorable = doc
isDoc.status = 0
isDoc.Read( )
Takie triki w programowaniu pozwalają w dynamiczny sposób przypisywać zmiennym referencje do obiektów
różnych klas, kiedy jedynym wymaganym ograniczeniem jest, aby wszystkie obiekty posiadały metody danego
interfejsu. Można więc zadeklarować:
IStorable isDoc As New IStorable( )
Sprawdzanie interfejsu
Aby mieć pewność, że we wszystkich miejscach, gdzie referencjom do interfejsów przypisano obiekty
implementujące te interfejsy należy włączyć opcję Option Strict On.
Jeśli się tego nie zrobi, może zdarzyc się, że program skompiluje się poprawnie, jednak podczas
wykonywania zgłosi błąd (gdyż obiekt nie będzie posiadał metody interfejsu, której się od niego
oczekiwało):
System.InvalidCastException: Specified cast is not valid.
Aby więc uniknąć takich sytuacji dobrze jest zapytać, czy dany obiekt dysponuje właściwym
interfejsem. Robi się to za pomocą operatora Is. Odpowiednim wyrażeniem jest
TypeOf espression Is type
Przykład:
Option Strict On
Imports System
Namespace InterfaceDemo
Interface IStorable
Sub Read()
Sub Write(ByVal obj As Object)
Property Status() As Integer
End Interface 'IStorable
' here's the new interface
Interface ICompressible
Sub Compress()
Sub Decompress()
End Interface 'ICompressible
' document implements only IStorable
Public Class Document
Implements IStorable
' the document constructor
Public Sub New(ByVal s As String)
Console.WriteLine("Creating document with: {0}", s)
End Sub 'New
' implement IStorable
Public Sub Read() Implements IStorable.Read
Console.WriteLine("Implementing the Read Method for IStorable")
End Sub 'Read
Public Sub Write(ByVal o As Object) Implements IStorable.Write
Console.WriteLine( _
"Implementing the Write Method for IStorable")
End Sub 'Write
Public Property Status() As Integer Implements IStorable.Status
Get
Return Status
End Get
Set(ByVal Value As Integer)
Status = Value
End Set
End Property
' hold the data for IStorable's Status property
Private myStatus As Integer = 0
End Class 'Document
Class Tester
Public Sub Run()
Dim doc As New Document("Test Document")
' only cast if it is safe
If TypeOf doc Is IStorable Then
Dim isDoc As IStorable = doc
isDoc.Read()
Else
Console.WriteLine("Could not cast to IStorable")
End If
' this test will fail
If TypeOf doc Is ICompressible Then
Dim icDoc As ICompressible = doc
icDoc.Compress()
Else
Console.WriteLine("Could not cast to ICompressible")
End If
End Sub 'Run
Shared Sub Main()
Dim t As New Tester
t.Run()
End Sub 'Main
End Class 'Tester
End Namespace 'InterfaceDemo
Wynik działania programu:
Creating document with: Test Document
Implementing the Read Method for IStorable
Could not cast to ICompressible
Rozszerzanie interfejsów
Rozszerzanie interfejsów jest niczym innym jak budowaniem wielokrotnego drzewa dziedziczenia (interfejs
może dziedziczyć po więcej niż jednym interfejsie bazowym).
Option Strict On
Imports System
Namespace InterfaceDemo
Interface IStorable
Sub Read()
Sub Write(ByVal obj As Object)
Property Status() As Integer
End Interface 'IStorable
' the Compressible interface is now the
' base for ICompressible2
Interface ICompressible
Sub Compress()
Sub Decompress()
End Interface 'ICompressible
' extend ICompressible to log the bytes saved
Interface ICompressible2
Inherits ICompressible
Sub LogSavedBytes()
End Interface 'ICompressible2
' Document implements both interfaces
Public Class Document
Implements ICompressible2, IStorable
' the document constructor
Public Sub New(ByVal s As String)
Console.WriteLine("Creating document with: {0}", s)
End Sub 'New
' implement IStorable
Public Sub Read() Implements IStorable.Read
Console.WriteLine("Implementing the Read Method for IStorable")
End Sub 'Read
Public Sub Write(ByVal o As Object) Implements IStorable.Write
Console.WriteLine( _
"Implementing the Write Method for IStorable")
End Sub 'Write
Public Property Status() As Integer Implements IStorable.Status
Get
Return myStatus
End Get
Set(ByVal Value As Integer)
myStatus = Value
End Set
End Property
' implement ICompressible
Public Sub Compress() Implements ICompressible.Compress
Console.WriteLine("Implementing Compress")
End Sub 'Compress
Public Sub Decompress() Implements ICompressible.Decompress
Console.WriteLine("Implementing Decompress")
End Sub 'Decompress
' implement ICompressible2
Public Sub LogSavedBytes() Implements ICompressible2.LogSavedBytes
Console.WriteLine("Implementing LogSavedBytes")
End Sub 'LogSavedBytes
' hold the data for IStorable's Status property
Private myStatus As Integer = 0
End Class 'Document
Class Tester
Public Sub Run()
Dim doc As New Document("Test Document")
If TypeOf doc Is IStorable Then
Dim isDoc As IStorable = doc
isDoc.Read()
Else
Console.WriteLine("Could not cast to IStorable")
End If
If TypeOf doc Is ICompressible2 Then
Dim ilDoc As ICompressible2 = doc
Console.Write("Calling both ICompressible and ")
Console.WriteLine("ICompressible2 methods...")
ilDoc.Compress()
ilDoc.LogSavedBytes()
Else
Console.WriteLine("Could not cast to ICompressible2")
End If
If TypeOf doc Is ICompressible Then
Dim icDoc As ICompressible = doc
Console.WriteLine( _
"Treating the object as Compressible... ")
icDoc.Compress()
Else
Console.WriteLine("Could not cast to ICompressible")
End If
End Sub 'Run
Shared Sub Main()
Dim t As New Tester
t.Run()
End Sub 'Main
End Class 'Tester
End Namespace 'InterfaceDemo
Wynik działania programu:
Creating document with: Test Document
Implementing the Read Method for IStorable
Calling both ICompressible and ICompressible2 methods...
Implementing Compress
Implementing LogSavedBytes
Treating the object as Compressible...
Implementing Compress
Inny przykład (rozszerzanie interfejsu z dziedziczeniem wielokrotnym):
Jeśli mamy interfejs:
Interface IStorableCompressible
Inherits IStorable, ICompressible2
Sub LogOriginalSize()
End Interface
oraz klasę dokument:
Public Class Document
Implements IStorableCompressible
można zrobić co następuje:
If TypeOf doc Is IStorable Then
Dim isDoc As IStorable = doc
If TypeOf doc Is ICompressible Then
Dim icDoc As ICompressible = doc
If TypeOf doc Is ICompressible2 Then
Dim ic2Doc As ICompressible2 = doc
If TypeOf doc Is IStorableCompressible Then
Dim iscDoc As IStorableCompressible = doc
Zaś zmienne zadeklarowane powyżej można wykorzystać tak:
isDoc.Read( )
icDoc.Compress( )
ic2Doc.LogSavedBytes( )
iscDoc.LogOriginalSize( )
Nadpisywanie metod interfejsów
Metody interfejsów można nadpisywać tak, jak to się robi z metodami klas.
Option Strict On
Imports Microsoft.VisualBasic
Imports System
Namespace OverridingInterfaces
Interface IStorable
Sub Read()
Sub Write()
End Interface
' simplify Document to implement only IStorable
Public Class Document : Implements IStorable
' the document constructor
Public Sub New(ByVal s As String)
Console.WriteLine("Creating document with: {0}", s)
End Sub
' make read virtual
Public Overridable Sub Read() Implements IStorable.Read
Console.WriteLine("Document Virtual Read Method for IStorable")
End Sub
' NB: Not virtual!
Public Sub Write() Implements IStorable.Write
Console.WriteLine("Document Write Method for IStorable")
End Sub
End Class
' derive from Document
Public Class Note : Inherits Document
Public Sub New(ByVal s As String)
MyBase.New(s)
Console.WriteLine("Creating note with: {0}", s)
End Sub
' override the Read method
Public Overrides Sub Read()
Console.WriteLine("Overriding the Read method for Note!")
End Sub
' implement my own Write method
Public Shadows Sub Write()
Console.WriteLine("Implementing the Write method for Note!")
End Sub
End Class
Class Tester
Public Sub Run()
' create a Document object
Dim theNote As Document = New Note("Test Note")
' cast the Document to IStorable
If TypeOf theNote Is IStorable Then
Dim isNote As IStorable = theNote
isNote.Read()
isNote.Write()
End If
Console.WriteLine(vbCrLf)
' direct call to the methods
theNote.Read()
theNote.Write()
Console.WriteLine(vbCrLf)
' create a note object
Dim note2 As New Note("Second Test")
' Cast the note to IStorable
If TypeOf note2 Is IStorable Then
Dim isNote2 As IStorable = note2
isNote2.Read()
isNote2.Write()
End If
Console.WriteLine(vbCrLf)
' directly call the methods
note2.Read()
note2.Write()
End Sub
Public Shared Sub Main()
Dim t As New Tester
t.Run()
End Sub
End Class
End Namespace
Wynik działania programu:
Creating document with: Test Note
Creating note with: Test Note
Overriding the Read method for Note!
Document Write Method for IStorable
Overriding the Read method for Note!
Document Write Method for IStorable
Creating document with: Second Test
Creating note with: Second Test
Overriding the Read method for Note!
Document Write Method for IStorable
Overriding the Read method for Note!
Implementing the Write method for Note!

Podobne dokumenty