Visual Basic设计模式应用实例
单例模式在 Visual Basic 中的应用
单例模式确保一个类只有一个实例,并提供一个全局访问点。在许多应用场景中,比如数据库连接池、日志记录器等,只需要一个实例来管理资源,避免重复创建带来的开销。
实现方式
在 Visual Basic 中实现单例模式,通常有两种常见方式:饿汉式和懒汉式。
饿汉式:在类加载时就创建实例。
Public Class Singleton
'私有静态字段存储唯一实例
Private Shared instance As Singleton = New Singleton()
'私有构造函数,防止外部实例化
Private Sub New()
End Sub
'公共静态方法提供全局访问点
Public Shared Function GetInstance() As Singleton
Return instance
End Function
End Class
在上述代码中,instance
字段在类加载时就被初始化。由于构造函数是私有的,外部代码无法通过 New
关键字创建新的实例,只能通过 GetInstance
方法获取唯一实例。
懒汉式:在第一次使用时才创建实例。
Public Class Singleton
'私有静态字段存储唯一实例
Private Shared instance As Singleton
'私有构造函数,防止外部实例化
Private Sub New()
End Sub
'公共静态方法提供全局访问点
Public Shared Function GetInstance() As Singleton
If instance Is Nothing Then
instance = New Singleton()
End If
Return instance
End Function
End Class
懒汉式的优点在于延迟实例化,只有在真正需要时才创建对象,节省资源。但在多线程环境下,上述代码存在线程安全问题。当多个线程同时调用 GetInstance
方法时,可能会创建多个实例。
为了解决线程安全问题,可以使用 SyncLock
关键字。
Public Class Singleton
'私有静态字段存储唯一实例
Private Shared instance As Singleton
'私有构造函数,防止外部实例化
Private Sub New()
End Sub
'公共静态方法提供全局访问点
Public Shared Function GetInstance() As Singleton
SyncLock GetType(Singleton)
If instance Is Nothing Then
instance = New Singleton()
End If
End SyncLock
Return instance
End Function
End Class
SyncLock
确保在同一时间只有一个线程可以进入临界区,从而避免了多线程环境下的实例重复创建问题。
应用场景
- 数据库连接池:数据库连接是一种昂贵的资源,创建过多连接会消耗大量系统资源。使用单例模式创建数据库连接池实例,可以有效地管理连接资源,避免重复创建连接。
- 日志记录器:在应用程序中,通常只需要一个日志记录器实例来记录所有的日志信息。单例模式可以保证日志记录的一致性,并且避免多个日志记录器实例可能带来的冲突。
工厂模式在 Visual Basic 中的应用
工厂模式提供了一种创建对象的方式,将对象的创建和使用分离。这种模式有助于提高代码的可维护性和可扩展性,尤其是在对象创建过程复杂或者需要根据不同条件创建不同类型对象的情况下。
简单工厂模式
简单工厂模式定义了一个工厂类,用于创建产品对象。它通常包含一个创建对象的静态方法,根据传入的参数决定创建哪种具体的产品对象。
假设我们有一个图形绘制的应用,需要绘制不同类型的图形,如圆形和矩形。
首先,定义图形的抽象基类:
Public MustInherit Class Shape
Public MustOverride Sub Draw()
End Class
然后,定义具体的图形类:
Public Class Circle
Inherits Shape
Public Overrides Sub Draw()
Console.WriteLine("绘制圆形")
End Sub
End Class
Public Class Rectangle
Inherits Shape
Public Overrides Sub Draw()
Console.WriteLine("绘制矩形")
End Sub
End Class
接着,定义简单工厂类:
Public Class ShapeFactory
Public Shared Function CreateShape(shapeType As String) As Shape
Select Case shapeType.ToLower()
Case "circle"
Return New Circle()
Case "rectangle"
Return New Rectangle()
Case Else
Return Nothing
End Select
End Function
End Class
在客户端代码中,可以这样使用:
Module Program
Sub Main()
Dim circle As Shape = ShapeFactory.CreateShape("circle")
If circle IsNot Nothing Then
circle.Draw()
End If
Dim rectangle As Shape = ShapeFactory.CreateShape("rectangle")
If rectangle IsNot Nothing Then
rectangle.Draw()
End If
End Sub
End Module
简单工厂模式的优点是实现简单,将对象创建逻辑封装在工厂类中,客户端只需要关心使用对象,而不需要关心对象的创建过程。缺点是不符合开闭原则,如果需要添加新的图形类型,就需要修改工厂类的代码。
工厂方法模式
工厂方法模式将对象的创建延迟到子类中。在工厂方法模式中,抽象工厂类定义了一个创建对象的抽象方法,具体的工厂子类实现这个方法来创建具体的产品对象。
修改上述代码,首先定义抽象工厂类:
Public MustInherit Class AbstractShapeFactory
Public MustOverride Function CreateShape() As Shape
End Class
然后定义具体的工厂子类:
Public Class CircleFactory
Inherits AbstractShapeFactory
Public Overrides Function CreateShape() As Shape
Return New Circle()
End Function
End Class
Public Class RectangleFactory
Inherits AbstractShapeFactory
Public Overrides Function CreateShape() As Shape
Return New Rectangle()
End Function
End Class
在客户端代码中:
Module Program
Sub Main()
Dim circleFactory As New CircleFactory()
Dim circle As Shape = circleFactory.CreateShape()
If circle IsNot Nothing Then
circle.Draw()
End If
Dim rectangleFactory As New RectangleFactory()
Dim rectangle As Shape = rectangleFactory.CreateShape()
If rectangle IsNot Nothing Then
rectangle.Draw()
End If
End Sub
End Module
工厂方法模式符合开闭原则,当需要添加新的图形类型时,只需要创建新的具体工厂子类,而不需要修改现有的工厂类代码。但工厂方法模式的缺点是工厂子类过多时,代码会变得复杂,不易维护。
抽象工厂模式
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
假设我们不仅要绘制图形,还需要为图形填充颜色。定义颜色的抽象类和具体颜色类:
Public MustInherit Class Color
Public MustOverride Sub Fill()
End Class
Public Class RedColor
Inherits Color
Public Overrides Sub Fill()
Console.WriteLine("填充红色")
End Sub
End Class
Public Class BlueColor
Inherits Color
Public Overrides Sub Fill()
Console.WriteLine("填充蓝色")
End Sub
End Class
定义抽象工厂类,它创建图形和颜色:
Public MustInherit Class AbstractFactory
Public MustOverride Function CreateShape() As Shape
Public MustOverride Function CreateColor() As Color
End Class
定义具体的工厂子类:
Public Class CircleRedFactory
Inherits AbstractFactory
Public Overrides Function CreateShape() As Shape
Return New Circle()
End Function
Public Overrides Function CreateColor() As Color
Return New RedColor()
End Function
End Class
Public Class RectangleBlueFactory
Inherits AbstractFactory
Public Overrides Function CreateShape() As Shape
Return New Rectangle()
End Function
Public Overrides Function CreateColor() As Color
Return New BlueColor()
End Function
End Class
在客户端代码中:
Module Program
Sub Main()
Dim circleRedFactory As New CircleRedFactory()
Dim circle As Shape = circleRedFactory.CreateShape()
Dim redColor As Color = circleRedFactory.CreateColor()
If circle IsNot Nothing AndAlso redColor IsNot Nothing Then
circle.Draw()
redColor.Fill()
End If
Dim rectangleBlueFactory As New RectangleBlueFactory()
Dim rectangle As Shape = rectangleBlueFactory.CreateShape()
Dim blueColor As Color = rectangleBlueFactory.CreateColor()
If rectangle IsNot Nothing AndAlso blueColor IsNot Nothing Then
rectangle.Draw()
blueColor.Fill()
End If
End Sub
End Module
抽象工厂模式的优点是将对象的创建和使用进一步分离,客户端只需要关心产品的接口,而不需要关心具体的实现。缺点是实现复杂,当产品族的结构发生变化时,修改成本较高。
策略模式在 Visual Basic 中的应用
策略模式定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。
实现步骤
- 定义抽象策略类:定义一个抽象类或接口,作为所有具体策略的基类,包含一个公共的方法用于执行具体的策略。
- 实现具体策略类:实现抽象策略类中定义的方法,每个具体策略类代表一种具体的算法。
- 定义上下文类:上下文类包含一个对抽象策略类的引用,通过构造函数或属性设置具体的策略对象,并提供一个方法来调用策略对象的方法。
假设我们有一个电商系统,根据不同的促销活动计算商品的价格。
首先,定义抽象策略类:
Public MustInherit Class DiscountStrategy
Public MustOverride Function CalculateDiscount(price As Double) As Double
End Class
然后,实现具体策略类:
Public Class NoDiscountStrategy
Inherits DiscountStrategy
Public Overrides Function CalculateDiscount(price As Double) As Double
Return price
End Function
End Class
Public Class PercentageDiscountStrategy
Inherits DiscountStrategy
Private percentage As Double
Public Sub New(pct As Double)
percentage = pct
End Sub
Public Overrides Function CalculateDiscount(price As Double) As Double
Return price * (1 - percentage)
End Function
End Class
Public Class FixedAmountDiscountStrategy
Inherits DiscountStrategy
Private fixedAmount As Double
Public Sub New(amount As Double)
fixedAmount = amount
End Sub
Public Overrides Function CalculateDiscount(price As Double) As Double
Return Math.Max(0, price - fixedAmount)
End Function
End Class
接着,定义上下文类:
Public Class ShoppingCart
Private discountStrategy As DiscountStrategy
Public Sub New(strategy As DiscountStrategy)
discountStrategy = strategy
End Sub
Public Function CalculateTotalPrice(price As Double) As Double
Return discountStrategy.CalculateDiscount(price)
End Function
End Class
在客户端代码中:
Module Program
Sub Main()
'无折扣策略
Dim noDiscountCart As New ShoppingCart(New NoDiscountStrategy())
Dim originalPrice As Double = 100
Dim totalPrice1 As Double = noDiscountCart.CalculateTotalPrice(originalPrice)
Console.WriteLine($"无折扣时总价: {totalPrice1}")
'百分比折扣策略,20% 折扣
Dim percentageDiscountCart As New ShoppingCart(New PercentageDiscountStrategy(0.2))
Dim totalPrice2 As Double = percentageDiscountCart.CalculateTotalPrice(originalPrice)
Console.WriteLine($"20% 折扣时总价: {totalPrice2}")
'固定金额折扣策略,折扣 30
Dim fixedAmountDiscountCart As New ShoppingCart(New FixedAmountDiscountStrategy(30))
Dim totalPrice3 As Double = fixedAmountDiscountCart.CalculateTotalPrice(originalPrice)
Console.WriteLine($"固定金额 30 折扣时总价: {totalPrice3}")
End Sub
End Module
应用场景
- 算法替换:当系统中有多种算法,并且需要在运行时根据不同条件动态切换算法时,策略模式非常适用。比如在排序算法中,根据数据规模和特点选择不同的排序策略。
- 避免多重条件判断:如果在代码中存在大量基于条件的判断来选择不同的行为,使用策略模式可以将这些行为封装到具体策略类中,使代码更加清晰和易于维护。
观察者模式在 Visual Basic 中的应用
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会通知所有观察者对象,使它们能够自动更新。
实现方式
- 定义主题接口:包含注册、移除和通知观察者的方法。
- 实现具体主题类:实现主题接口,维护一个观察者列表,并在状态变化时通知观察者。
- 定义观察者接口:包含一个更新方法,用于接收主题的通知。
- 实现具体观察者类:实现观察者接口的更新方法,在接收到通知时执行相应的操作。
假设我们有一个股票市场的应用,当股票价格发生变化时,需要通知关注该股票的投资者。
首先,定义主题接口:
Public Interface ISubject
Sub Attach(observer As IObserver)
Sub Detach(observer As IObserver)
Sub Notify()
End Interface
然后,实现具体主题类:
Public Class StockSubject
Implements ISubject
Private observers As New List(Of IObserver)
Private stockPrice As Double
Public Sub New(price As Double)
stockPrice = price
End Sub
Public Property Price As Double
Get
Return stockPrice
End Get
Set(value As Double)
stockPrice = value
Notify()
End Set
End Property
Public Sub Attach(observer As IObserver) Implements ISubject.Attach
observers.Add(observer)
End Sub
Public Sub Detach(observer As IObserver) Implements ISubject.Detach
observers.Remove(observer)
End Sub
Public Sub Notify() Implements ISubject.Notify
For Each observer In observers
observer.Update(Me)
Next
End Sub
End Class
接着,定义观察者接口:
Public Interface IObserver
Sub Update(subject As ISubject)
End Interface
最后,实现具体观察者类:
Public Class InvestorObserver
Implements IObserver
Private name As String
Public Sub New(investorName As String)
name = investorName
End Sub
Public Sub Update(subject As ISubject) Implements IObserver.Update
Dim stockSubject As StockSubject = TryCast(subject, StockSubject)
If stockSubject IsNot Nothing Then
Console.WriteLine($"{name} 收到通知,股票价格变为 {stockSubject.Price}")
End If
End Sub
End Class
在客户端代码中:
Module Program
Sub Main()
Dim stock As New StockSubject(100)
Dim investor1 As New InvestorObserver("投资者 1")
Dim investor2 As New InvestorObserver("投资者 2")
stock.Attach(investor1)
stock.Attach(investor2)
stock.Price = 105
stock.Detach(investor2)
stock.Price = 110
End Sub
End Module
应用场景
- 事件处理:在图形用户界面(GUI)开发中,当一个组件的状态发生变化时,需要通知其他相关组件进行更新,观察者模式可以很好地实现这种机制。
- 消息推送:在消息系统中,当有新消息到达时,需要推送给所有订阅该消息的用户,观察者模式可以实现这种一对多的消息通知功能。
装饰器模式在 Visual Basic 中的应用
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。它通过创建一个装饰类,将原始对象包装起来,并在装饰类中添加新的行为。
实现步骤
- 定义组件接口:定义一个接口或抽象类,作为被装饰对象和装饰器的共同基类,包含需要扩展的方法。
- 实现具体组件类:实现组件接口,代表原始的对象。
- 定义装饰器抽象类:实现组件接口,并包含一个对组件接口的引用,通过构造函数初始化该引用。
- 实现具体装饰器类:继承装饰器抽象类,在装饰器类中添加新的功能,并调用被装饰对象的方法。
假设我们有一个文本显示的应用,需要对文本进行不同的装饰,如添加边框、加粗等。
首先,定义组件接口:
Public Interface ITextComponent
Function Display() As String
End Interface
然后,实现具体组件类:
Public Class PlainText
Implements ITextComponent
Private text As String
Public Sub New(t As String)
text = t
End Sub
Public Function Display() As String Implements ITextComponent.Display
Return text
End Function
End Class
接着,定义装饰器抽象类:
Public MustInherit Class TextDecorator
Implements ITextComponent
Protected component As ITextComponent
Public Sub New(c As ITextComponent)
component = c
End Sub
Public MustOverride Function Display() As String Implements ITextComponent.Display
End Class
最后,实现具体装饰器类:
Public Class BorderDecorator
Inherits TextDecorator
Public Sub New(c As ITextComponent)
MyBase.New(c)
End Sub
Public Overrides Function Display() As String
Dim borderLine As String = New String("*", component.Display.Length + 4)
Return $"{borderLine}{Environment.NewLine}* {component.Display} *{Environment.NewLine}{borderLine}"
End Function
End Class
Public Class BoldDecorator
Inherits TextDecorator
Public Sub New(c As ITextComponent)
MyBase.New(c)
End Sub
Public Overrides Function Display() As String
Return $"**{component.Display}**"
End Function
End Class
在客户端代码中:
Module Program
Sub Main()
Dim plainText As New PlainText("Hello, World!")
Console.WriteLine(plainText.Display())
Dim borderedText As New BorderDecorator(plainText)
Console.WriteLine(borderedText.Display())
Dim boldBorderedText As New BoldDecorator(borderedText)
Console.WriteLine(boldBorderedText.Display())
End Sub
End Module
应用场景
- 动态添加功能:当需要在运行时为对象添加新功能,而又不想修改对象的原有结构时,装饰器模式非常适用。比如在图像处理中,动态为图像添加滤镜效果。
- 减少子类数量:如果通过继承来扩展对象的功能,会导致子类数量急剧增加。装饰器模式可以通过组合的方式,在不增加过多子类的情况下实现功能扩展。
代理模式在 Visual Basic 中的应用
代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象可以在客户端和目标对象之间起到中介作用,对客户端的请求进行预处理或对目标对象的响应进行后处理。
实现方式
- 定义主题接口:定义客户端和代理都需要实现的接口,包含客户端调用的方法。
- 实现真实主题类:实现主题接口,代表实际执行操作的对象。
- 实现代理类:实现主题接口,包含对真实主题对象的引用,并在方法中对真实主题的方法调用进行控制和处理。
假设我们有一个远程服务调用的场景,为了减少网络开销,在本地缓存数据。
首先,定义主题接口:
Public Interface IRemoteService
Function GetData() As String
End Interface
然后,实现真实主题类:
Public Class RemoteServiceImpl
Implements IRemoteService
Public Function GetData() As String Implements IRemoteService.GetData
'模拟远程服务调用
System.Threading.Thread.Sleep(2000)
Return "从远程服务获取的数据"
End Function
End Class
接着,实现代理类:
Public Class RemoteServiceProxy
Implements IRemoteService
Private realService As RemoteServiceImpl
Private cachedData As String
Public Function GetData() As String Implements IRemoteService.GetData
If cachedData Is Nothing Then
If realService Is Nothing Then
realService = New RemoteServiceImpl()
End If
cachedData = realService.GetData()
End If
Return cachedData
End Function
End Class
在客户端代码中:
Module Program
Sub Main()
Dim proxy As New RemoteServiceProxy()
Dim data1 As String = proxy.GetData()
Console.WriteLine(data1)
Dim data2 As String = proxy.GetData()
Console.WriteLine(data2)
End Sub
End Module
在上述代码中,代理类 RemoteServiceProxy
缓存了远程服务调用的结果,当客户端再次调用 GetData
方法时,直接返回缓存的数据,减少了远程服务调用的开销。
应用场景
- 远程代理:用于访问远程对象,如上述的远程服务调用场景,通过代理对象封装远程调用的细节,提供本地统一的访问接口。
- 虚拟代理:当创建真实对象开销较大时,代理对象可以在需要时才创建真实对象,如加载大图片时,先使用代理显示一个占位符,当真正需要显示图片时再加载真实图片。
- 保护代理:根据客户端的权限控制对真实对象的访问,代理对象可以在调用真实对象方法前进行权限验证。