1. Trang chủ
  2. » Công Nghệ Thông Tin

Visual Basic 2005 Design and Development - Chapter 7 pps

42 319 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Design Patterns
Tác giả Gamma, Helm, Johnson, Vlissides
Trường học Wiley
Chuyên ngành Visual Basic
Thể loại Chương
Năm xuất bản 2005
Thành phố Indianapolis
Định dạng
Số trang 42
Dung lượng 740,28 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The Factory pattern uses one class to create instances ofother classes.. The following code shows how a program might make a clone of a Customerobject named old_customer: Dim new_custome

Trang 1

Design Patterns

A design pattern consists of one or more objects that together perform some useful task In a sense,

a design pattern is an algorithm for using classes to do something useful It’s a recipe for buildingpart of an application A design pattern may include objects from a single class, or an ensemble ofclasses working together

As you refine your application’s design, you should start looking for common design patterns.You may discover a group of classes that implement a pattern You may also find a group ofclasses that you could rewrite to follow a pattern In either case, if you can rewrite your design tofollow a design pattern, then you can take advantage of the solutions that other developers havedeveloped to solve the same problem That can save you time and gives you some confidence thatthe solution will work correctly so you can move on to design other parts of the application.Some patterns also make an application easier to implement, debug, and maintain For example,the Adapter and Facade patterns described later in this chapter help decouple classes so they areless tightly related That lets you build, debug, and modify the classes with less impact on the rest

of the application

Since the seminal book Design Patterns by Gamma et al (Boston: Addison-Wesley, 1995) was

pub-lished, all sorts of patterns have been devised for data access, integration, interoperability, services,security, Web Services, configuration, exceptions, testing, threat modeling, legacy applications,project management, and many more This chapter, or even this whole book, doesn’t have room tocover them all Rather than just listing the names of a bunch of design patterns with no explana-tion, this chapter describes in more detail some of the patterns that I have found most useful in theapplications that I’ve built

A lot of the design pattern books treat their patterns in a very formal way with introductory tions for each pattern giving a synopsis, context, “forces,” and other sections eventually leading up

sec-to a solution, which is often so simple it’s anticlimactic In some cases, the prelude sections are sostilted and full of technical diagrams that they’re practically unintelligible This chapter takes amuch more intuitive approach

Trang 2

For a more complete treatment of design patterns, see a book entirely about them such as the original

Design Patterns This book was written by four authors (Gamma, Helm, Johnson, and Vlissides), who are

sometimes called “the gang of four,” so the book is also sometimes called “the gang of four,” or GOF

You can also see the book Visual Basic Design Patterns by Mark Grand and Brad Merrill (Indianapolis:

Wiley, 2005)

The patterns that I find most useful can be grouped into three categories: creation patterns, relationpatterns, and behavior patterns

Creation Patterns

Creation patterns are patterns that deal with how objects are created The Clone pattern provides a way

to make new objects by copying existing ones The Factory pattern uses one class to create instances ofother classes

Clone

Sometimes it’s useful to make an exact copy of an existing object That’s what a clone is: a copy of anobject that is initialized with the same property values as the object from which it was cloned

The Clone pattern is usually called the Prototype pattern in the design pattern literature The idea is that

the application can make a new object based on a prototype object by cloning the prototype

It’s relatively easy to allow cloning in Visual Basic Simply add a Clonefunction to a class that returns anew instance of the class with the same parameters as the existing object

In fact, Visual Basic’s Objectclass provides a MemberwiseClonemethod that returns a new object withthe same property values as the original object Because all classes are descendants of the Objectclass,they can all have the MemberwiseClonemethod

The following code shows how a program might make a clone of a Customerobject named

old_customer:

Dim new_customer As Customer = DirectCast(old_customer.MemberwiseClone, Customer)

The MemberwiseClonemethod returns a generic Objectso this code uses DirectCastto convert theresult into a Customerobject

To make cloning objects a little easier, you can make a Clonemethod that wraps up the call to

MemberwiseClone The following code shows how a simple Customerclass can use MemberwiseClone

to implement a Clonemethod:

Public Class Customer

‘ Code omitted

‘ Return a shallow clone

Public Function Clone() As Customer

Trang 3

Return DirectCast(Me.MemberwiseClone(), Customer)End Function

End Class

Now the main program can use this method, as in the following code:

Dim new_customer As Customer = old_customer.Clone()

The MemberwiseClonemethod makes a new copy of an object and initializes its fields to the same ues used by the original object That works well for simple data types such as Integeror Stringbut itdoesn’t work as well with references to other objects

val-For example, suppose an Orderitem contains a reference to a Customerobject When you clone an

Order, should the clone contain a reference to the same Customerobject as the original, or should itcontain a reference to a new Customerobject that has been cloned from the original? If the new Order

has a reference to the existing object, then changes to the joint Customerobject are shared by both

Orders If the new Orderobject has a reference to a new copy of the Customer, then changes to the two

Customerobjects happen independently

A clone that uses the same references as the original object is called a shallow copy The MemberwiseClone

method makes shallow copies A clone that uses references to new objects is called a deep copy.

You could also define partially deep copies if you want to For example, suppose an Orderitem has erences to a Customerobject, and a collection of references to OrderItemobjects that describe the items that are included in the order You might want an Orderclone to share the same Customer

ref-object as the original, but get its own new OrderItemscollection This would let you easily make a new order for an existing customer This kind of partially deep clone would be defined for a particular application, so there aren’t really any standards for defining them.

The following code shows an Orderclass that can make shallow or deep copies:

Public Class OrderPublic OrderCustomer As CustomerPublic OrderItems As New List(Of OrderItem)

‘ Code omitted

‘ Return a deep or shallow clone

Public Function Clone(ByVal deep As Boolean) As Order

‘ Start with a shallow clone

Dim new_order As Order = DirectCast(Me.MemberwiseClone(), Order)

‘ If appropriate, copy deeper data

If deep ThenWith new_order

‘ Clone the OrderCustomer

.OrderCustomer = Me.OrderCustomer.Clone()

‘ Clone the OrderItems list

.OrderItems = New List(Of OrderItem)For Each order_item As OrderItem In Me.OrderItems.OrderItems.Add(order_item.Clone())

Trang 4

Next order_itemEnd With

End IfReturn new_orderEnd Function

End Class

The Orderclass contains a reference to a Customerobject and a generic list of OrderItemobjects

The Clonemethod takes a parameter indicating whether it should return a shallow or deep copy Thefunction starts by using MemberwiseCloneto make a shallow copy If it should make a deep copy, itthen sets the new object’s Customerand OrderItemreferences to clones of the original objects

In this example, the Customerand OrderItemclasses don’t contain any references, so they only provideshallow copies, and the Orderclass’s Clonemethod doesn’t need to pass a parameter into the Customer

and OrderItemclass’s Clonemethods If those classes did support shallow and deep cloning, you wouldprobably want to pass the same deep parameter into those methods

The following code shows how the Clonesexample program demonstrates cloning in the Customer

and OrderItemsclasses:

Private Sub Form1_Load(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles MyBase.Load

Dim txt_originals As String = “”

Dim txt_clones As String = “”

‘ Make and clone a Customer

Dim cust1 As New Customer(“Rod”, “Stephens”, “1337 Leet St”, “Bugsville”, _

“AZ”, “87654”)Dim cust2 As Customer = cust1.Clone()txt_originals &= “*** cust1:” & vbCrLf & cust1.ToString() & “ -” & _vbCrLf

txt_clones &= “*** cust2:” & vbCrLf & cust2.ToString() & “ -” & vbCrLf

‘ Make and clone an Order

Dim order1 As New OrderWith order1

.OrderCustomer = New Customer(“Bob”, “Hacker”, “123 Decompile Ct”, _

“Attatash”, “ME”, “01234”).OrderItems.Add(New OrderItem(“Cookies”, 12)).OrderItems.Add(New OrderItem(“Pencil”, 2)).OrderItems.Add(New OrderItem(“Notebook”, 6))End With

‘ Make a shallow clone

Dim order2 As Order = order1.Clone(False)

‘ Make a deep clone

Dim order3 As Order = order1.Clone(True)

‘ Modify the original Order object

With order1.OrderCustomer

Trang 5

.FirstName = “Mindy”

.LastName = “Modified”

.Street = “12 Altered Ave”

.City = “Changed City”

.State = “AL”

.Zip = “98765”

End WithWith order1.OrderItems(0).Item = “Rice (grain)”

.Quantity = 10000End With

‘ Display the three Orders

txt_originals &= “*** order1: “ & vbCrLf & order1.ToString() & _

“ -” & vbCrLftxt_clones &= “*** order2: “ & vbCrLf & order2.ToString() & _

“ -” & vbCrLftxt_originals &= “*** order1: “ & vbCrLf & order1.ToString() & _

“ -” & vbCrLftxt_clones &= “*** order3: “ & vbCrLf & order3.ToString() & _

“ -” & vbCrLf

‘ Display the results

txtOriginals.Text = txt_originalstxtClones.Text = txt_clonestxtOriginals.Select(0, 0)txtClones.Select(0, 0)End Sub

The code first creates Customerobject Its constructor initializes its properties The code uses the

Customerclass’s Clonemethod to make a copy of the object It then uses the objects’ ToStringods to add textual representations of the objects to a pair of output strings

meth-Next, the program makes an Orderitem, attaches a Customerobject, and fills its OrderItemslist with

OrderItemobjects It then makes shallow and deep clones of the Orderobject It modifies some of thevalues in the original object, and then adds textual representations of all three objects to the outputstrings The code finishes by displaying the result strings

Figure 7-1 shows the result Notice that the Customerobject on the top left is the same as its clone on theright The original Orderitem named order1on the left matches the shallow copy order2on the right.The deep copy order3does not match, because it got its own new Customerand OrderItemobjects when

it was copied, so those items were not modified when the program altered the original Order’s data

Visual Basic defines an ICloneableinterface to make cloning more consistent Unfortunately, the face defines a Clonemethod, but it doesn’t take a parameter indicating whether it should be a deep orshallow copy, and it returns a generic Objectinstead of a more specific object type

inter-The intent is that the program will use MemberwiseCloneto make a shallow copy, and Cloneto make adeep copy In either case, the main program must use DirectCastto convert the returned Objectinto

an appropriate type

Trang 6

Figure 7-1: Program Clonesdemonstrates shallow and deep clones.

The ICloneableinterface doesn’t provide much advantage over simply writing your own Clone

method that takes a parameter indicating the kind of copy to make and returning a specific object type.Unless you need to work with another class that requires the ICloneableinterface, you are just as welloff writing your own Clonemethod

Factory

A factory is a class that provides a means for creating instances of other classes The main program

cre-ates the Factoryobject and calls a method to create an instance of another class Usually the factorycould return more than one type of object depending on the circumstances Pulling the decision-makingprocess into the class helps isolate the main program from information about those created classes.Suppose it’s hard to decide what type of object to create Perhaps the type of object depends on theuser’s selections, the value of a calculation, or the data in a file In that case, the program must performsome sort of complicated test, and then create the appropriate object

For example, suppose the user opens a file that might be a bitmap, text file, or database The programmust create a different kind of object to handle each choice If file_infois a FileInfoobject represent-ing the selected file, then you could use code similar to the following to create the right object Here,

FileProcessoris a parent class from which BitmapProcessor, TextFileProcessor, MdbProcessor,and OtherProcessorare derived:

‘ Make an appropriate FileProcessor

Dim file_processor As FileProcessor = Nothing

Select Case file_info.Extension.ToLower

Trang 7

file_processor = New MdbProcessor(file_info)Case Else

file_processor = New OtherProcessor(file_info)End Select

‘ Tell the FileProcessor to do its thing

fp.ProcessFile()

The Select Casestatement examines the file’s extension and creates an object of the appropriate

FileProcessorsubclass The program then calls the processor’s ProcessFilemethod to take action.This code works, but it locks information about the file types into the main program’s code If you laterneeded to change the types of files supported, or the method by which the program decides which type

of processor to build, you must update the main program

You can pull the intelligence out of the main program into its own class by making a Factoryclass The

FileProcessorFactoryclass shown in the following code holds the logic to create different kinds of

FileProcessorsubclasses:

‘ Make various kinds of FileProcessor objects

Public Class FileProcessorFactoryPublic Function MakeFileProcessor(ByVal file_name As String) As FileProcessorDim file_info As New FileInfo(file_name)

‘ See what kind of file this is

Dim file_processor As FileProcessor = NothingSelect Case file_info.Extension.ToLowerCase “.bmp”

file_processor = New BitmapProcessor(file_info)Case “.txt”

file_processor = New TextFileProcessor(file_info)Case “.mdb”

file_processor = New MdbProcessor(file_info)Case Else

file_processor = New OtherProcessor(file_info)End Select

Return file_processorEnd Function

Dim fp As FileProcessor = fp_factory.MakeFileProcessor(dlgOpen.FileName)

‘ Tell the FileProcessor to do its thing

fp.ProcessFile()

Trang 8

The code makes a new FileProcessorFactoryobject and calls its MakeFileProcessorfunction

to make an object from one of the FileProcessorsubclasses It then calls that object’s ProcessFile

method

Notice that the factory creates instances of classes that all inherit from the FileProcessorbase class.

In general, the objects that the factory creates should either come from the same ancestor class, or ment the same interface so the main program can do something meaningful with them Otherwise, the

imple-program will need to use some sort of test such as If TypeOf returned_object Is Class1 Then and so forth That pulls logic about the subclasses back into the main program and defeats much of the

purpose of the factory.

For another example, suppose you build a base class named ErrorProcessor The subclass EmailErrorProcessorsends error messages to a developer and the subclass LogFileErrorProcessorlogs errormessages into a file When an error occurs, the program checks environment variables to see whether itshould log errors into a file or send email If it should send email, it checks other variables to decide towhom it should send the mail You could put all of these tests in the main program and repeat themevery time there is an error message, but it would be better to make an ErrorProcessorFactorytocreate an object from the appropriate ErrorProcessorsubclass

I have also seen programs where developers made a factory class for each concrete class that they willwant to create For example, a CustomerFactoryobject creates a Customerobject Sometimes there may

be a FactoryBaseclass from which the specific factory classes inherit The FactoryBaseclass woulddefine a CreateInstancefunction or some other method for creating an instance of the concrete class

In this scenario, you can use the factory objects to defer creating the concrete objects For example, you canpass a CustomerFactoryobject into a subroutine that may need to make a Customerobject If the routinedecides it doesn’t need a Customerobject, it doesn’t need to create one If the Customerclass is compli-cated and hard to initialize, not creating a Customercan save the program some time and memory

Similarly, you can pass a factory object into a subroutine that can later use it to create an object from theconcrete class, all without knowing what type of object it is creating In this approach, the decision aboutthe type of object was made earlier when you picked the appropriate factory class, but actually creatingthe object has been deferred

One misuse of factory classes that I’ve seen is when the program uses a set of factory classes to ately create instances of their classes For example, the program creates an InvoiceFactoryobject andthen immediately uses it to create an Invoiceobject In that case, you could just as well have movedany initialization code from the InvoiceFactoryinto the Invoiceclass’s constructors Another varia-tion would be to give the Invoiceclass a public shared function that returns an Invoiceobject Either

immedi-of these solutions avoids having to have the InvoiceFactoryclass, avoids the need to create instances

of that class, and moves logic dealing with the Invoiceclass inside that class where it belongs

Relation Patterns

Relation patterns deal with the ways in which objects are related to each other They affect the

object-oriented structure of the application The Adapter, Facade, and Interface patterns all provide front-ends

to classes They allow one class to interact with one or more other classes in as simpler, more isolatedway That can help decouple the classes so they are easier to write, debug, and maintain separately

Trang 9

An Adapter class provides an interface between two classes It lets two classes cooperate when they

other-wise couldn’t because they have mismatched interfaces

For example, suppose you have written an event logging system that can log messages into a file oremail messages to a developer Now, suppose you decide you also want to be able to send messages to

a pager The method your code uses to log messages into a file or send them in an email doesn’t workthe same way as the Web Service that you would call to send the message to a pager

To make the existing classes work with the new pager class, you could write an Adapter between them.The Adapter would let the main program call methods in the same way it does now when using a log file

or email It would then pass requests along to the Web Service in whatever format it requires Figure 7-2shows the situation graphically

Figure 7-2: An Adapter allows a program to work with an incompatible class

Because the Adapter wraps up another object, in this case a Web Service, it is also sometimes called aWrapper

The Adapter pattern is closely related to the Interface and Facade patterns

Facade

A Facade is basically an interface to a large and potentially complicated collection of objects that perform a

single role For example, a program might use DataGatherer, ReportFormatter, and ReportPrinter

classes to build and print reports You can make printing reports simpler by hiding the process behind aFacade Figure 7-3 shows the idea graphically

Figure 7-3: A Facade presents a simple interface for a complex collection of objects

Application

DataGatherer

RepeatFormatterReportPrinter

PagerServiceAdapter

MessageLogger

Trang 10

Note that the classes do not need to be strictly contained in the Facade For example, the DataGatherer

class could be used in other operations, perhaps to build a report for display rather than printing In that case, you might want another Facade to hide the details of report display.

Interface

An Interface is basically the same as an Adapter, but it is used for a different purpose You use an Adapter

to allow existing incompatible classes to work together You use an Interface to isolate two classes sothey can evolve independently It helps keep two classes loosely coupled

For example, suppose your application generates a series of reports with a standard format Early in theproject, you may not have completely finalized the report format or the data that will be displayed inthe reports If you make the CustomerOrderclass call the ReportGeneratorclass directly, then youwill need to change the CustomerOrdercode whenever the ReportGeneratorcode changes Whenthe data stored in the CustomerOrderclass changes, you may also need to make changes to the

to display the new data

However, the Interface lets you change CustomerOrderwithout changing ReportGeneratorrightaway The developers working on that class can continue their work independently, and process the newdata when they are ready to do so

I’ve worked on a couple of projects where tight coupling between classes made it very difficult for ers working on the classes to get anything done The first class would change and the second class would break until it was updated The next day the second class would change and first would break until it was fixed Adding an Interface between the two allowed them both to move forward more quickly.

develop-Behavior Patterns

Behavior patterns deal with the behavior of parts of the application The Abstract Base Class pattern uses

a base class to determine the properties and behavior of derived classes

Chain of Responsibility and Chain of Events allow a program to apply a series of methods to an object orevent until one of them handles it

The Command pattern lets you treat a method as an object that you can pass around to different pieces

of code Commands can be useful for implementing the Chain of Responsibility, Chain of Events, andStrategy patterns

Trang 11

Delegation is a technique where one object uses other objects to provide some of its behavior The Model,View, Controller pattern allows a group of relatively simple classes to let the user view and modify data incomplex ways Both the Delegation and the Model, View, Controller patterns deal with ways classes canwork together to simplify complex behavior.

The Property Procedure pattern describes a standard method for implementing properties that makesthem easier to debug

The Snapshot pattern describes a technique for allowing code to save an object’s state and later restore it

to that state without knowing the object’s internal details

Finally, the Strategy pattern encapsulates a strategy or algorithm While the Chain of Responsibility andChain of Events apply several methods until one works, the Strategy pattern allows a program to apply

a single one of a number of different approaches

Abstract Base Class

The idea behind an Abstract Base Class is so simple that it’s almost not worth describing as a design

pattern However, it is an important idea, so it’s probably better to give it too much attention rather thannot enough

When you derive a set of child classes from a parent class, the program can treat objects from the child

classes as if they were members of the parent class That’s the object-oriented principle of polymorphism.

For example, suppose you are making a drawing application and you want to make classes representingdrawn objects such as rectangles, ellipses, polygons, stars, text, and so forth Each of these has someshared characteristics such as an (X, Y) position, width, height, fill color, outline color, and line style

If the objects are to draw themselves, they must also provide some sort of Drawmethod

To allow the main program to treat all of these objects in the same way, you can pull out these commoncharacteristics and place them in a parent class named Shape Now the program can store objects in acollection of Shapes It can loop through the collection calling each method’s Drawmethod and it canexamine each object’s size and position

While the program must be able to create objects from the ellipse, rectangle, and other child classes, itwouldn’t make much sense to create a Shapeobject The Shapeclass represents an abstraction of thechild classes, not a particular shape itself It can store a position, size, and colors, but if you called its

Drawmethod, what would it draw?

To ensure that the program doesn’t try to create a Shapeobject, you can make the class abstract An abstract class is one that cannot be instantiated In contrast, a concrete class is one that is not abstract, so

you can create instances of it In Visual Basic, you can make a class abstract by adding the MustInherit

keyword to its declaration

The following code shows an abstract Shapeclass:

‘ The abstract Shape parent class

Public MustInherit Class ShapePublic Location As RectanglePublic FillColor As Color

Trang 12

Public ForeColor As Color

‘ The constructor is protected so the child classes must

‘ provide constructors for the program to use

‘ Save the location and colors

Public Sub New(ByVal shape_location As Rectangle, ByVal fill_color As Color, _ByVal fore_color As Color)

Location = shape_locationFillColor = fill_colorForeColor = fore_colorEnd Sub

The Shapeclass defines public Location, FillColor, and ForeColorvariables that are shared by all

of its child classes It then defines a constructor that uses parameters to initialize these variables

If you declare a constructor as Protected, only that class and any derived classes can use it, so the main program will not be able to use the constructor If this is the only constructor, then the program cannot make an instance of the class This essentially makes the class abstract.

Next, the Shapeclass declares a Drawmethod The MustOverridekeyword makes this is an abstract method declaration that provides no implementation (notice that there is no End Substatement) Any con-crete ancestor classes must override this method and provide a concrete implementation The declaration

in the Shapeclass defines the signature of the method In this case, the method must be a subroutine, not

a function, and must take a single Graphicsobject as a parameter

Note that a class with an abstract method must be an abstract class In Visual Basic, that means if the class has a MustOverridemethod, then it must be declared MustInherit.

Note also that only concrete child classes must provide an implementation of the abstract method If

you declare a child class MustInherit, then it doesn’t need to override the method Its descendants can do it instead.

The following code shows the concrete RectangleShapeclass derived from the Shapeclass:

‘ Rectangle

Public Class RectangleShape

Inherits Shape

‘ Constructor Delegated to the Shape class

Public Sub New(ByVal shape_location As Rectangle, ByVal fill_color As Color, _ByVal fore_color As Color)

MyBase.New(shape_location, fill_color, fore_color)End Sub

Trang 13

‘ Draw the rectangle.

Public Overrides Sub Draw(ByVal gr As System.Drawing.Graphics)Using the_brush As New SolidBrush(FillColor)

gr.FillRectangle(the_brush, Location)End Using

Using the_pen As New Pen(ForeColor)gr.DrawRectangle(the_pen, Location)End Using

End SubEnd Class

The RectangleShapeclass defines a constructor that uses the Shapeclass’s constructor to save locationand color information

The class provides a concrete Drawmethod It makes a brush and fills the rectangle; then makes a penand outlines the rectangle

You can easily make other classes to draw ellipses and other shapes The EllipseShapeclass would beidentical to the RectangleShapeclass, except the Drawmethod would draw an ellipse instead of a rect-angle Other classes might need additional information For example, a PolygonShapeclass would need

to store the points it will connect ATextShapeclass would need to know what font to use and whattext to draw

The following code shows how the AbstractBaseClassexample program works:

Imports System.Collections.GenericPublic Class Form1

‘ The collection of Shapes to draw

Private m_Shapes As New List(Of Shape)Private Sub Form1_Load(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles MyBase.Load

‘ Make some Shapes

m_Shapes.Add(New RectangleShape(New Rectangle(50, 20, 50, 120), _Color.Orange, Color.Red))

m_Shapes.Add(New EllipseShape(New Rectangle(80, 60, 90, 180), _Color.Blue, Color.DarkBlue))

m_Shapes.Add(New RectangleShape(New Rectangle(20, 80, 210, 40), _Color.Lime, Color.DarkGreen))

m_Shapes.Add(New EllipseShape(New Rectangle(10, 150, 250, 50), _Color.Pink, Color.Teal))

‘ Redraw

Me.Invalidate()End Sub

‘ Draw the shapes

Private Sub Form1_Paint(ByVal sender As Object, _ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Painte.Graphics.Clear(Me.BackColor)

For Each a_shape As Shape In m_Shapesa_shape.Draw(e.Graphics)

Trang 14

Next a_shapeEnd Sub

End Class

The program declares a generic List Of Shapeobjects to store drawing objects When the form loads, theprogram creates some RectangleShapeand EllipseShapeobjects and adds them to the collection

When the form’s Paintevent handler fires, the program clears the form and loops through the Shape

collection, calling each object’s Drawmethod

Figure 7-4 shows the program in action

Figure 7-4: Program AbstractBaseClassuses concrete child classes to draw rectangles and ellipses

An abstract base class is useful for specifying features that must be implemented by child classes, andwhen it doesn’t make sense for the program to create instances of the base class

Chain of Responsibility

In the Chain of Responsibility pattern, some sort of message, task, or other item that must be handled is

sent to a series of objects until one of them processes it This frees the code that must handle the itemfrom needing to know how the item must be handled

Normally, the classes that process the item provide a function that examines the item and returns Trueifthe object completely handled the item

The following code shows an abstract NumberHandlerclass The abstract WasHandledfunction returns

Trueor Falseto indicate whether the object processed an integer parameter

Public MustInherit Class NumberHandler

Public MustOverride Function WasHandled(ByVal value As Integer) As BooleanEnd Class

Trang 15

Child classes must override the WasHandledfunction The following code shows a PowerHandlerclassthat displays a message if the input value is a power (greater than 1) of some other integer (for example,

16 = 2 ^ 4):

Public Class PowerHandlerInherits NumberHandler

‘ Return True if we handle this item

Public Overrides Function WasHandled(ByVal value As Integer) As Boolean

If value < 0 Then value = -value

If value < 4 Then Return FalseFor base As Integer = 2 To CInt(Sqrt(value))Dim power As Double = Log(value, base)

If CInt(power) = power Then

‘ value = base ^ power

MessageBox.Show(value.ToString() & “ = “ & _base.ToString() & “^” & power.ToString(), _

“Power”, MessageBoxButtons.OK, MessageBoxIcon.Information)Return True

End IfNext base

‘ Not a power

Return FalseEnd FunctionEnd Class

The WasHandledfunction first ensures that the input value is non-negative If the value is less than 4,the function returns False, because the values 0 through 3 are not powers of other integers

Next, the program loops through base values between 2 and the square root of the input value It takesthe logarithm of the value using the base If the result is an integer, then the value is a power of the base,

so the function displays a message and returns True

If the input value is not a power of any of the bases, it is not a power, so the function returns False

Example program ChainOfCommanduses the following code to evaluate numbers:

Public Class Form1Private m_Handlers As New List(Of NumberHandler)

‘ Make the chain of command

Private Sub Form1_Load(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles MyBase.Loadm_Handlers.Add(New PrimeHandler())

m_Handlers.Add(New PowerHandler())m_Handlers.Add(New EvenHandler())m_Handlers.Add(New UnhandledHandler())End Sub

‘ Process a number

Private Sub btnProcess_Click(ByVal sender As System.Object, _

Trang 16

ByVal e As System.EventArgs) Handles btnProcess.ClickDim value As Integer = Integer.Parse(txtValue.Text)For Each handler As NumberHandler In m_Handlers

If handler.WasHandled(value) Then Exit ForNext handler

End SubEnd Class

When the program’s form loads, the code makes a List of NumberHandlerobjects and adds subclasses

of the NumberHandlerclass to the list PrimeHandlerprocesses prime numbers, PowerHandler

processes numbers that are powers of other numbers, and EvenHandlerprocesses even numbers

The UnhandledHandlerclass processes every number by displaying a message box saying the numberwas unhandled It’s usually a good idea to place a similar catch-all object at the end of the chain of com-mand to deal with any items that are not processed earlier At a minimum, this object can display anerror message to the user or log the event

If you enter a value in the ChainOfCommandexample program and click the Process button, the codeloops through the list of handlers It calls each handler’s WasProcessedfunction and exits the loop ifthe function returns True

This example tries each of its handlers in the order in which they were added to the handler list, but youcould apply the handlers in any order that makes sense For example, you might sort the handlers bypriority and apply the higher priority items first

For a concrete example, suppose you have created classes that process error messages in various ways.Perhaps you would first like to try sending an email to developers, then try writing the error into a logfile, and, finally, display a message to the user You can give each of the objects a priority and sort the list

of handlers so that the program tries them in priority order

Note that an object’s WasHandledfunction should return Trueonly if the item is completely handled so

that other handlers don’t need to look at it An object’s WasHandledfunction might take some action topartially handle the item, and then return Falseto give other objects a chance to perform additionalprocessing

For example, suppose when there’s an error you want to notify the user and log the file in some way.Then the NotifyUserErrorHandler’s WasHandledmethod could display a message and return False.Next the program would let the EmailErrorHandlerand LogErrorHandlerobjects also process themessage

Chain of Events

A Chain of Events is similar to a Chain of Command, but it is somewhat more specific A Chain of

Command sends an item to a series of handler objects to see if any of them can process the item A Chain

of Events sends an event to a series of event handlers to allow them to handle it

Trang 17

The ChainOfEventsSimpleexample program uses the NumberProcessorclass shown in thefollowing code to monitor an integer value When its value is changed, the NumberProcessorraises a

NumberChangedevent

Public Class NumberProcessorPrivate m_Value As IntegerPublic Property Value() As IntegerGet

Return m_ValueEnd Get

Set(ByVal value As Integer)m_Value = value

RaiseEvent NumberChanged(m_Value)End Set

End PropertyPublic Delegate Sub NumberChangedDelegate(ByVal value As Integer)Public Event NumberChanged As NumberChangedDelegate

End Class

When the program starts, it uses the following code to register three event handlers for the

NumberChangedevent:

Private m_NumberProcessor As New NumberProcessor

‘ Add events to the NumberProcessor

Private Sub Form1_Load(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles MyBase.LoadAddHandler m_NumberProcessor.NumberChanged, AddressOf CheckPrimeAddHandler m_NumberProcessor.NumberChanged, AddressOf CheckPowerAddHandler m_NumberProcessor.NumberChanged, AddressOf CheckEvenm_NumberProcessor.Value = Integer.Parse(txtValue.Text)

End Sub

If you change the value in the program’s text box, the following code updates the NumberProcessor, itraises its NumberChangedevent, and the three event handlers execute:

‘ Set the new integer value

Private Sub txtValue_TextChanged(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles txtValue.TextChanged

If IsNumeric(txtValue.Text) Thenm_NumberProcessor.Value = Integer.Parse(txtValue.Text)End If

Trang 18

determine the order in which they are executed, although in practice, they are executed in the order inwhich they were registered, so you have some control They also don’t give you the option of stoppingafter an event handler has successfully processed the event.

You can provide these extra features if you use a custom event The following code shows a new version

of the NumberProcessorclass that provides a custom NumberChangedevent:

Public Class NumberProcessor

Private m_Value As IntegerPublic Property Value() As IntegerGet

Return m_ValueEnd Get

Set(ByVal value As Integer)m_Value = value

RaiseEvent NumberChanged(m_Value, False)End Set

End PropertyPublic Delegate Sub NumberChangedDelegate(ByVal value As Integer, _ByRef was_handled As Boolean)

Private m_EventDelegates As New List(Of NumberChangedDelegate)Public Custom Event NumberChanged As NumberChangedDelegateAddHandler(ByVal value As NumberChangedDelegate)m_EventDelegates.Add(value)

End AddHandlerRemoveHandler(ByVal value As NumberChangedDelegate)m_EventDelegates.Remove(value)

End RemoveHandlerRaiseEvent(ByVal value As Integer, ByRef was_handled As Boolean)was_handled = False

For Each a_delegate As NumberChangedDelegate In m_EventDelegatesa_delegate(value, was_handled)

If was_handled Then Exit ForNext a_delegate

End RaiseEventEnd Event

End Class

A custom event handler has three sections: AddHandler, RemoveHandler, and RaiseEvent

The AddHandlersection stores information about a delegate that is being registered This exampleadds the delegate to a list

The RemoveHandlersection removes a delegate from the list

The RaiseEventsection invokes the delegates when the NumberProcessorobject raises its NumberChangedevent In this example, the NumberChangedDelegateis declared with two parameters: a valuepassed by valueand a Boolean variable named was_handledpassed by reference The invoked eventhandler should set this to Trueif it has completely processed the event

Trang 19

In this example, the RaiseEventcode loops through the stored delegates, invoking each until one of thesets was_handledto True That lets the program skip any remaining event handlers.

Example program ChainOfEventsuses event handlers that check a number to see whether it is prime,

a power, or even If any of the event handlers takes action, it displays an appropriate message and sets

was_handledto Trueso that the program skips the remaining event handlers

Custom events don’t give you an easy way to prioritize event handlers (other than the fact that they areexecuted in the registration order) If you need to order the handlers more dynamically, use the Chain ofCommand pattern described in the previous section

Command

In the Command design pattern, an object represents an action The program can perform the action by

calling the object’s Executemethod

Command objects are useful when you want to pass the action around for use later, and you don’t want thecode that is carrying and using the object to know the details of what the command does This decreasesthe coupling between the code and the action, and makes working with both easier

For example, suppose a report generator class gathers data and formats a report You could pass thegenerator a command object that provides a ProcessReportmethod After it has built the report,the generator can call the command object’s ProcessReportmethod without knowing whether thereport will be printed, saved into a file, or emailed to a user

You can pass a variety of command objects to allow for contingencies For example, you could pass anorder processing routine an object to invoke if the customer has an unpaid balance, another object toinvoke if the requested items are out of stock, a third object to invoke if the order is processed success-fully, and so forth

Visual Basic provides a couple of ways you can implement the Command design pattern The more mon approach is to build a class that provides an Executemethod, instantiate the class, and pass theresulting object to the code that needs it

com-The following code shows a simple command class Its constructor takes a parameter, giving the sage that the object will later display, and saves the message in a private variable The Executemethoddisplays the following message:

mes-Public Class ExecuteClassPrivate m_Message As StringPublic Sub New(ByVal message As String)m_Message = message

End SubPublic Sub Execute()MessageBox.Show(m_Message, “ExecuteClass”, _MessageBoxButtons.OK, MessageBoxIcon.Asterisk)End Sub

End Class

Trang 20

A second approach is to pass a delegate to a routine that can invoke it later With this method you don’tneed to build an extra class just to perform a simple action This is more confusing to many program-mers, however.

The Commandsexample program uses the following code to demonstrate each of these methods:

Public Class Form1

Private Delegate Sub ExecuteDelegate()Private Sub Form1_Load(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles MyBase.LoadbtnGoodDay.Tag = New ExecuteDelegate(AddressOf SayGoodDay)btnGutenTag.Tag = New ExecuteClass(“Guten Tag”)

End SubPrivate Sub SayGoodDay()MessageBox.Show(“Good Day”, “SayGoodDay”, _MessageBoxButtons.OK, MessageBoxIcon.Asterisk)End Sub

Private Sub btnGoodDay_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles btnGoodDay.ClickDim btn As Button = DirectCast(sender, Button)Dim execute_delegate As ExecuteDelegate = _DirectCast(btn.Tag, ExecuteDelegate)execute_delegate()

End SubPrivate Sub btnGutenTag_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles btnGutenTag.ClickDim btn As Button = DirectCast(sender, Button)Dim execute_object As ExecuteClass = DirectCast(btn.Tag, ExecuteClass)execute_object.Execute()

End SubEnd Class

The program defines the ExecuteDelegatetype as a reference to a simple subroutine that takes noparameters When the program starts, its Loadevent handler sets the Good Day button’s Tagproperty

to a delegate representing the form’s SayGoodDaysubroutine

Next, the program sets the Guten Tag button’s Tagproperty to a new instance of the ExecuteClass

When you click the Good Day button, the button’s event handler converts the event’s sender into thebutton that caused the event It casts the button’s Tagproperty into an ExecuteDelegateand executes

it This delegate refers to the SayGoodDaysubroutine so that routine runs and displays the message

“Good Day.”

When you click the Guten Tag button, the button’s event handler converts the event’s sender into thebutton that caused the event It casts the button’s Tagproperty into an ExecuteClassobject and callsits Executemethod That method displays the message “Guten Tag” stored by the object’s constructorwhen it was created in the form’s Loadevent handler

Trang 21

Delegates are useful when you need to pass a single routine in a fairly local context and you don’t want

to create a new class Most developers find delegates more confusing than objects, however, so you mayprefer to use objects

Delegation

Like abstract base classes, delegation is a simple but important technique Delegation is the process of one

object using another to provide features It gives a result similar to those of inheritance, but withoutactually using inheritance

To provide Delegation in Visual Basic, give one class a reference to another class To provide the features

of the second class, the object calls the methods provided by its referenced object

For example, suppose you’ve built a Houseclass that has the properties NumberOfBedrooms,

NumberOfBathrooms, SquareFeet, and PricePerSquareFoot It also has a CalculateValue

function that returns the house’s estimated value based on its square feet and price per square foot.Suppose you also have a Vehicleclass that has the properties MilesPerGallon, FuelCapacity,andFuelRemaining Vehiclealso has a CalculateRangefunction that uses MilesPerGallonand

FuelRemainingto estimate the distance the Vehiclecan travel before running out of fuel

Now, suppose you want to make a MotorHomeclass that has the characteristics of both a Houseand a

Vehicle Visual Basic does not allow multiple inheritance, so MotorHomecannot inherit from bothclasses

One solution to this problem is to make MotorHomeinherit from the Houseclass Then give it a privatevariable of type Vehicle To provide the features of a Vehicle, make the MotorHomecall the propertiesand methods provided by this private object

Figure 7-5 shows the idea graphically

Figure 7-5: In Delegation, an object uses other objects to provide its behavior

MotorHome

Vehicle Object Vehicle Methods House Methods

Ngày đăng: 14/08/2014, 11:20

TỪ KHÓA LIÊN QUAN