2015-03-26 2 views
2

У меня есть два объекта, которые должны взаимодействовать друг с другом, называется Collateral, другой называется Model. Model является абстрактным классом, реализуется Model_A, Model_B, Model_AB. Каждый объект обеспечения имеет коллекцию models как одно из своих свойств. Чтобы инициализировать каждый Model, мне нужно будет использовать информацию от Collateral (и еще один объект позволяет назвать его User_Input), эта информация будет отличаться в зависимости от реализации Model.Как инициализировать объект с помощью свойств инициализирующего объекта в VBA

Мой вопрос: можно ли использовать конструктор, который будет знать, какой объект его создает (в этом случае Model Конструктор, который знает, что именно Collateral его экземпляр)? Если я не предполагаю, что кто-то предложит мне использовать абстрактный заводский шаблон, если так, то как бы это выглядело (боюсь, я все еще зеленый, когда дело касается ООП)?

Для простоты предположим, что следующее:

  • Collateral обладает свойствами А, В, С, Models_Collection
  • Collateral вызовы процедур Run для каждого из Models она создала (имеет в Models_Collection)
  • Model имеет public Sub под названием Run, который реализован во всех классах ниже
  • Процедура Run Manipula тес Collateral
  • Model_A требует свойство A для инициализации
  • Model_B требует свойство В инициализировать
  • Model_AB требует свойство A, B, чтобы инициализировать

Вот упрощенный код, как я предполагаю, что это должно выглядеть :

Сопутствующий

Dim A, B, C as Variant 
Dim Model_Collection as Collection 
Sub New_Model(Model_Type as String) 
    Model_Collection.Add(Model_Implementation) 
End Sub 
Sub Execute_Models() 
    For Each Model in Model_Collection 
     Model.Run(Me) 
    Next Model 
End Sub 

Модель

Sub Run() 
    End 

Model_A

Implements Model 
Sub Class_Initialize() 
    'Some code that takes property A from Collateral that Created this object 
Sub Run(Collateral as Collateral) 
    'Some Code 
End Sub 

Model_B

Implements Model 
Sub Class_Initialize() 
    'Some code that takes property B from Collateral that Created this object 
Sub Run(Collateral as Collateral) 
    'Some Code 
End Sub 

Model_AB

Implements Model 
Sub Class_Initialize() 
    'Some code that takes property A, and B from Collateral that Created this object 
Sub Run(Collateral as Collateral) 
    'Some Code 
End Sub 
+1

Мне нравится то, что вы делаете - ООП в VBA всегда приятно видеть. У VBA нет конструкторов, поэтому вы правы, вам понадобятся фабрики; 'Class_Initialize' не может быть параметризован и выполняется перед любым другим кодом в классе, поэтому вы не можете использовать его как конструктор. –

+0

Просто немного разъяснить. Вы хотите, чтобы «Залог» создал объекты «Model»? Было бы приемлемо создавать их за пределами «Залога» и передавать их в него? – RubberDuck

+0

Mat's Mug: Спасибо за быстрый ответ, можете ли вы предположить, что фабрика (и) будет выглядеть так: – sgp667

ответ

3

Во-первых, позволяет ответить на ваш вопрос. Как вы можете динамически создавать экземпляры другого класса, которые реализуют один и тот же интерфейс? Как уже указывалось, у VBA нет конструкторов, поэтому вы правы. Здесь вызывается заводской шаблон.

Как я склоняюсь к этому, это определение общего перечисления в классе Interface, которое отслеживает, какие классы были реализованы. Каждый раз, когда вы реализуете новый, вам нужно добавить его в свой enum и Factory.Это немного больше обслуживания, чем мне нравится, но без правильного размышления мы не можем с этим поделать.

Итак, IModel интерфейс:

Public Enum EModel 
    ModelA 
    ModelB 
    ModelC 
End Enum 

Public Sub Run 
End Sub 

Ваши модели сами по себе остаются неизменными. Затем вернитесь в свой Collateral. Внесите свой New_Model вот так.

private models as Collection 

Public Sub New_Model(ByVal type As EModel) As IModel 
    dim model As IModel 
    Select Case type 
     Case EModel.ModelA: Set model = New ModelA 
     Case EModel.ModelB: Set model = New ModelB 
     Case EModel.ModelC: Set model = New ModelC 
    End Select 

    models.Add model 
End Sub 

Обратите внимание, что лучше использовать перечисление, чем строка, как в вашем примере, поэтому он получает во время компиляции проверяется на наличие ошибок вместо выполнения. (Это устраняет вероятность неправильного написания чего-то.)


Если он меня реализации этого, я хотел бы создать реальный отдельный класс ModelFactory. Затем Collateral обратился к фабрике моделей, чтобы получить то, что ей нужно. Я думаю, это отличное разделение.

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

Public Function CreateModel(Optional A As Variant, Optional B As Variant, Optional C As Variant) 
    If Not A Is Nothing Then 
     If B Is Nothing Then 
      Set CreateModel = New ModelA 
      Exit Function 
     Else 
      Set CreateModel = New ModelC 
      Exit Function 
     End If 
    End If 

    If Not B Is Nothing Then 
     Set CreateModel = New ModelB 
     Exit Function 
    End If 
End Function 

Обратите внимание, что это полностью исключает перечисление и необходимость указывать тип. Фабрика знает, что создавать на основе того, какие аргументы доступны для нее.

Тогда ваш класс Collateral просто вызывает завод и дает ему все, что у него есть.

Private A,B,C 
Private models As Collection 
Private factory As ModelFactory 

Private Sub Class_Initialize() 
    Set factory = New ModelFactory 
End Sub 

Public Sub New_Model() 
    models.Add factory.CreateModel(A,B,C) 
End Sub 

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

Как я могу точно указать, какой тип модели у меня есть?

Ну, для этого у вас есть несколько вариантов, которые подробно описаны в this code review Q & A. Это зависит от вашего варианта использования, но вот они.

  • TypeName(arg) - Возвращает строковое имя объекта. Например:

    Dim model As IModel 
    Set model = New ModelA 
    
    Debug.Print TypeName(model) '=> "ModelA" 
    
  • TypeOf и Is - Проверяет тип переменной немного более сильно. Подробности в вопросе, с которым я связан, но вот пример.

    Dim model as IModel 
    Set model = SomeFunctionThatReturnsAnIModel() 
    
    If TypeOf model Is ModelA Then 
        ' take some specific action for ModelA types 
    Else If TypeOf model Is ModelB Then 
        ' ModelB type specific action 
    Else If ...