MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Visual Basic设计模式应用实例

2021-10-111.1k 阅读

单例模式在 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 确保在同一时间只有一个线程可以进入临界区,从而避免了多线程环境下的实例重复创建问题。

应用场景

  1. 数据库连接池:数据库连接是一种昂贵的资源,创建过多连接会消耗大量系统资源。使用单例模式创建数据库连接池实例,可以有效地管理连接资源,避免重复创建连接。
  2. 日志记录器:在应用程序中,通常只需要一个日志记录器实例来记录所有的日志信息。单例模式可以保证日志记录的一致性,并且避免多个日志记录器实例可能带来的冲突。

工厂模式在 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 中的应用

策略模式定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。

实现步骤

  1. 定义抽象策略类:定义一个抽象类或接口,作为所有具体策略的基类,包含一个公共的方法用于执行具体的策略。
  2. 实现具体策略类:实现抽象策略类中定义的方法,每个具体策略类代表一种具体的算法。
  3. 定义上下文类:上下文类包含一个对抽象策略类的引用,通过构造函数或属性设置具体的策略对象,并提供一个方法来调用策略对象的方法。

假设我们有一个电商系统,根据不同的促销活动计算商品的价格。

首先,定义抽象策略类:

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

应用场景

  1. 算法替换:当系统中有多种算法,并且需要在运行时根据不同条件动态切换算法时,策略模式非常适用。比如在排序算法中,根据数据规模和特点选择不同的排序策略。
  2. 避免多重条件判断:如果在代码中存在大量基于条件的判断来选择不同的行为,使用策略模式可以将这些行为封装到具体策略类中,使代码更加清晰和易于维护。

观察者模式在 Visual Basic 中的应用

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会通知所有观察者对象,使它们能够自动更新。

实现方式

  1. 定义主题接口:包含注册、移除和通知观察者的方法。
  2. 实现具体主题类:实现主题接口,维护一个观察者列表,并在状态变化时通知观察者。
  3. 定义观察者接口:包含一个更新方法,用于接收主题的通知。
  4. 实现具体观察者类:实现观察者接口的更新方法,在接收到通知时执行相应的操作。

假设我们有一个股票市场的应用,当股票价格发生变化时,需要通知关注该股票的投资者。

首先,定义主题接口:

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

应用场景

  1. 事件处理:在图形用户界面(GUI)开发中,当一个组件的状态发生变化时,需要通知其他相关组件进行更新,观察者模式可以很好地实现这种机制。
  2. 消息推送:在消息系统中,当有新消息到达时,需要推送给所有订阅该消息的用户,观察者模式可以实现这种一对多的消息通知功能。

装饰器模式在 Visual Basic 中的应用

装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。它通过创建一个装饰类,将原始对象包装起来,并在装饰类中添加新的行为。

实现步骤

  1. 定义组件接口:定义一个接口或抽象类,作为被装饰对象和装饰器的共同基类,包含需要扩展的方法。
  2. 实现具体组件类:实现组件接口,代表原始的对象。
  3. 定义装饰器抽象类:实现组件接口,并包含一个对组件接口的引用,通过构造函数初始化该引用。
  4. 实现具体装饰器类:继承装饰器抽象类,在装饰器类中添加新的功能,并调用被装饰对象的方法。

假设我们有一个文本显示的应用,需要对文本进行不同的装饰,如添加边框、加粗等。

首先,定义组件接口:

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

应用场景

  1. 动态添加功能:当需要在运行时为对象添加新功能,而又不想修改对象的原有结构时,装饰器模式非常适用。比如在图像处理中,动态为图像添加滤镜效果。
  2. 减少子类数量:如果通过继承来扩展对象的功能,会导致子类数量急剧增加。装饰器模式可以通过组合的方式,在不增加过多子类的情况下实现功能扩展。

代理模式在 Visual Basic 中的应用

代理模式为其他对象提供一种代理以控制对这个对象的访问。代理对象可以在客户端和目标对象之间起到中介作用,对客户端的请求进行预处理或对目标对象的响应进行后处理。

实现方式

  1. 定义主题接口:定义客户端和代理都需要实现的接口,包含客户端调用的方法。
  2. 实现真实主题类:实现主题接口,代表实际执行操作的对象。
  3. 实现代理类:实现主题接口,包含对真实主题对象的引用,并在方法中对真实主题的方法调用进行控制和处理。

假设我们有一个远程服务调用的场景,为了减少网络开销,在本地缓存数据。

首先,定义主题接口:

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 方法时,直接返回缓存的数据,减少了远程服务调用的开销。

应用场景

  1. 远程代理:用于访问远程对象,如上述的远程服务调用场景,通过代理对象封装远程调用的细节,提供本地统一的访问接口。
  2. 虚拟代理:当创建真实对象开销较大时,代理对象可以在需要时才创建真实对象,如加载大图片时,先使用代理显示一个占位符,当真正需要显示图片时再加载真实图片。
  3. 保护代理:根据客户端的权限控制对真实对象的访问,代理对象可以在调用真实对象方法前进行权限验证。