Прикладная реализация метода класса - PullRequest
0 голосов
/ 30 октября 2018

У меня есть библиотека (надстройка) с классом, который используется в нескольких небольших приложениях. Я хочу предоставить Save метод для этого класса, который будет зависеть от запущенного приложения.
Чтобы решить эту проблему, я пытаюсь использовать шаблон стратегии (возможно, я неправильно понимаю шаблон), но мое понимание предмета отсутствует. Во время выполнения я предоставляю класс стратегии, который будет обрабатывать сохранение. Общий класс предоставляет метод Save, который связывает его с предоставленным классом стратегии. Но для обеспечения согласованности мне кажется, что общий класс должен также реализовывать интерфейс стратегии.

Интерфейс IRecord (Common Class):

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

Реализация записи (общего класса):

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

Интерфейс и реализация стратегии:

  • IDatabaseStrategy: Public Function Save():End Function

  • DataBaseStrategyA:

    Implements IDatabaseStrategy
    Private Function IDataBaseStrategy_Save()
    Debug.Print "Saving to database A"
    End Function
    
  • DataBaseStrategyB:

    Implements IDatabaseStrategy
    Private Function IDataBaseStrategy_Save()
    Debug.Print "Saving to database B"
    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, а затем переписать Item из IRecord в IDatabaseStrategy, чтобы использовать его. Я думаю, что я использую шаблон неправильно, поэтому мой вопрос - как я могу предоставить DatabaseStrategy для Record, чтобы мне не пришлось переделывать объект и, возможно, без реализации IDatabaseStrategy в Record? Альтернативы, которые я рассмотрел:

  • Упаковка DatabaseStrategy вокруг Record, специфичная для Record и приложения (DatabaseStrategy.Create(Record).Save)
  • Представление DatabaseStrategy в качестве члена Record, но затем кажется, что приложение должно знать, что DatabaseStrategy является членом записи (Record.DatabaseStrategy.Save).

1 Ответ

0 голосов
/ 06 апреля 2019

Это очень хороший вопрос по design-patterns с VBA.

Полное описание strategy pattern можно увидеть здесь , но, резюмируя, это класс UML и диаграмма последовательности, описывающая его:

enter image description here

  • Резюме

По сути, эта стратегия позволяет алгоритму варьироваться независимо от клиентов, которые его используют.

Откладывание решения о том, какой алгоритм использовать до времени выполнения, позволяет вызывающему коду быть более гибким и многократно используемым.

Вы предложили эти два решения, и я хочу объяснить, почему я их не использовал:

  • Обтекание DatabaseStrategy вокруг записи, относящейся к записи и приложению (DatabaseStrategy.Create(Record).Save)

    Использование этого решения уменьшит сплоченность , поскольку class DatabaseStrategy придется позаботиться о создании экземпляра Record objects (что, как я покажу позже, это можно сделать, применив Factory-Pattern )

  • Представление DatabaseStrategy в качестве члена Записи, но затем кажется, что приложение должно знать, что DatabaseStrategy является членом записи (Record.DatabaseStrategy.Save)

    Этот шаблон проектирования ДОЛЖЕН знать стратегию, но новшеством является то, что он может быть изменен во время выполнения, передавая объект требуемой стратегии, который реализует методы действия. В этом случае я бы отодвинул DatabaseStrategy от Record, потому что последнее упомянутое - это object, которое нужно сохранить, но не агент выполняет действие (которое может быть Database class как я покажу позже).

Чтобы решить проблему создания экземпляра objects с помощью конструкторов VBA class, которые не принимают аргументы, я использовал другой шаблон: Factory Pattern .

enter image description here

Приведенный ниже код написан просто для иллюстрации того, как использовать эти два шаблона, поэтому многие функции опущены, и нет реальной реализации методов базы данных. Теперь вот остаток кода:

  • Код

IRecord class ( Интерфейс ):

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

Запись class:
Этот класс в основном представляет запись базы данных. Для простоты у нас есть только свойство value, но реальная реализация, конечно, будет другой.

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 ( Интерфейс )
Это стратегия интерфейса соединения. Здесь мало что изменилось из вашего кода.

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

ConnectionStrategyA class
Класс для стратегии А. Здесь мало что изменилось из вашего кода.

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
Класс для стратегии А. Здесь мало что изменилось из вашего кода.

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

База данных class
Это класс, который обрабатывает соединение с базой данных. Стратегия подключения определяет, как подключиться к базе данных.
Обратите внимание, что вы можете изменить стратегию без необходимости переделывать объект, просто вызовите 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 не имеет статического класса, поэтому используйте вместо него модуль)
Этот класс отвечает за создание вашей 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-pattern в сочетании с Factory-Pattern помогает вам исключить большую часть повторяющегося кода для инициализации и установить стратегию для вашей базы данных class.

Надеюсь, это поможет.

...