class 方法的特定于应用程序的实现

Application specific implementation of a class method

我有一个带有 class 的库(加载项),用于一些小型应用程序。我想为 class 提供一个 Save 方法,它 将取决于 运行.
的应用程序 为了解决这个问题,我正在尝试使用一种策略模式(我可能误解了这种模式),但我对这个主题的理解还不够。在运行时,我提供了一个策略 class 来处理保存。 Common class 公开了一个 Save 方法,该方法将其中继到提供的策略 class。但为了保持一致性,在我看来,常见的 class 也必须实现策略接口。

IRecord(通用Class)接口:

Public Function DoSomething(): End Function
Public Function SetStrategy(ByVal Strategy As IDatabaseStrategy): End Function

记录(通用Class)实现:

Private RecordStrategy As IDatabaseStrategy
Implements IRecord
Implements IDatabaseStrategy 'Implements this interface to have Save method
Private Function IRecord_DoSomething():
    'does whatever the class is supposed to do
End Function
Private Function IRecord_SetStrategy(ByVal Strategy As IDatabaseStrategy)
    Set RecordStrategy = Strategy
End Function
Private Function IDataBaseStrategy_Save()
    RecordStrategy.Save
End Function

策略接口和实现:

应用模块:

Option Explicit

Public Sub ApplicationA()
    Dim Item As IRecord
    Set Item = New Record
    Dim Strategy As IDatabaseStrategy
    Set Strategy = New DatabaseStrategyA
    Item.SetStrategy Strategy 'this would normally be done with constructor
    Dim ItemToSave As IDatabaseStrategy
    Set ItemToSave = Item
    ItemToSave.Save
End Sub

Public Sub ApplicationB()
    Dim Item As IRecord
    Set Item = New Record
    Dim Strategy As IDatabaseStrategy
    Set Strategy = New DatabaseStrategyB
    Item.SetStrategy Strategy 'this would normally be done with constructor
    Dim ItemToSave As IDatabaseStrategy
    Set ItemToSave = Item
    ItemToSave.Save
End Sub

使用这种方法,我必须让 Record 实现 Database strategy 才能使用 Save 方法,然后将 ItemIRecord 重铸为 IDatabaseStrategy 使用它。我想我使用的模式不正确,所以我的问题是 - 如何为 Record 提供 DatabaseStrategy,这样我就不必重铸对象,也可能无需实施 IDatabaseStrategyRecord? 我考虑过的替代方案:

这是关于 design-patternsVBA 的一个非常好的问题。

可以看到 strategy pattern 的完整描述 here,但总而言之,这是描述它的 UML class 和序列图:

  • 总结

基本上,这种策略让算法独立于使用它的客户端而变化。

Deferring the decision about which algorithm to use until runtime allows the calling code to be more flexible and reusable.

您提出了这两个解决方案,我想解释一下为什么我没有使用它们:

  • 环绕 DatabaseStrategy 特定于记录和应用程序的记录 (DatabaseStrategy.Create(Record).Save)

    Using this solution would decrease cohesion, beacuse the class DatabaseStrategy would have to take care of instantiating Record objects (which as I show later, this can be done applying Factory-Pattern)

  • DatabaseStrategy 公开为记录的成员,但应用程序似乎必须知道 DatabaseStrategy 是记录的成员 (Record.DatabaseStrategy.Save)

    This design pattern MUST know the strategy, but the innovation is that it can be changed at runtime, passing the desidered strategy object that implements the action methods. In this case I would move DatabaseStrategy away from Record, because the last-mentioned is the object that needs to be saved, but it's not the agent who performs the action (which could be a Database class as I'll show later).

为了解决如何使用不接受参数的 VBA class 构造函数实例化此 objects 的问题,我使用了另一种模式:Factory Pattern

下面的代码只是为了说明如何使用这两种模式而编写的,因此省略了很多功能,也没有真正实现数据库方法。现在这是剩下的代码:

  • 代码

IRecord class (Interface):

Public Function getValue() As String: End Function
Public Function setValue(stringValue As String): End Function

Record class:
This class basically represent a database record. To keep it simple, we only have a value property, but a real implementation would be different of course.

Implements IRecord
Private value As String

Private Function IRecord_getValue() As String
    IRecord_getValue = value
End Function

Private Function IRecord_setValue(stringValue As String)
    value = stringValue
End Function

IConnectionStrategy class (Interface)
This is the Interface of Connection strategy. Little is changed from your code here.

Public Function Save(ByVal record As IRecord): End Function

ConnectionStrategyA class
Class for Strategy A. Little is changed from your code here.

Implements IConnectionStrategy
Private connectionString As String

Private Sub Class_Initialize()
    connectionString = "DRIVER=Oracle Server;SERVER=myA.server.com\DatabaseA;"
End Sub

'Implements Strayegy A
Private Function IConnectionStrategy_Save(ByVal record As IRecord)
    Debug.Print "Saving to ", connectionString, "Record with value:", record.getValue
End Function

ConnectionStrategyB class
Class for Strategy A. Little is changed from your code here.

Implements IConnectionStrategy
Private connectionString As String

Private Sub Class_Initialize()
    connectionString = "DRIVER=SQL Server;SERVER=myB.server.com\DatabaseB;"
End Sub

'Implements Strategy B
Private Function IConnectionStrategy_Save(ByVal record As IRecord)
    Debug.Print "Saving to ", connectionString, "Record with value:", record.getValue
End Function

Database class
This is the class that handles the connection with a database. A connection strategy defines how to connect to the database.
Note that you can change the strategy without having to recast the object, just invoke setConnectionStrategy(...)

Private connection As IConnectionStrategy

Private Sub Class_Initialize()
    'Initialize everything but strategy.
End Sub

'Funzione che verrà richiamata per inizializzare le propietà della classe
Public Sub InitiateProperties(ByVal connectionStrategy As IConnectionStrategy)
    Set connection = connectionStrategy
End Sub

Public Sub saveRecord(ByVal record As IRecord)
    connection.Save record
End Sub

Public Sub setConnectionStrategy(ByVal strategy As IConnectionStrategy)
    connection = strategy
End Sub

Private Sub Class_Terminate()
    'close connections
End Sub

DatabaseFactory Module (VBA doesn't have static class, so use a Module instead)
This class is responsible for the instantiation of your Database Objects

'Instantiate a Database with given Strategy
Public Function createDatabaseWithStrategy(strategy As IConnectionStrategy) As Database
    Set createDatabaseWithStrategy = New Database
    CreateFoglioIdro.InitiateProperties connectionStrategy:=strategy
End Function

'Instantiate a Database with Strategy A
Public Function createDatabaseWithStrategyA() As Database
    Set createDatabaseWithStrategyA = New Database
    createDatabaseWithStrategyA.InitiateProperties connectionStrategy:=New ConnectionStrategyA
End Function

'Instantiate a Database with Strategy B
Public Function createDatabaseWithStrategyB() As Database
    Set createDatabaseWithStrategyB = New Database
    createDatabaseWithStrategyB.InitiateProperties connectionStrategy:=New ConnectionStrategyB
End Function

最后是一个应用示例 (App Module):

Option Explicit

Public Sub ApplicationA()
    Dim record As IRecord
    Set record = New record
    record.setValue ("This is a value")

    Dim db As Database
    Set db = DatabaseFactory.createDatabaseWithStrategyA
    db.saveRecord record
    'Prints-> Saving to     DRIVER=Oracle Server;SERVER=myA.server.com\DatabaseA;   Record with value:          This is a value
End Sub

Public Sub ApplicationB()
    Dim record As IRecord
    Set record = New record
    record.setValue ("This is a value")

    Dim db As Database
    Set db = DatabaseFactory.createDatabaseWithStrategyB
    db.saveRecord record
    'Prints-> Saving to     DRIVER=SQL Server;SERVER=myB.server.com\DatabaseB;      Record with value:          This is a value
End Sub

如您所见,strategy-patternFactory-Pattern 相结合,可帮助您消除大部分重复代码来初始化和设置数据库策略 class

希望这对您有所帮助。