Смена MVC (MVP) триад в приложении

Подскажите пожалуйста подход к реализации подобного приложения. Если кратко, то необходимо переключаться между несколькими триадами MVP (MVC). В зависимости от выбранной в выпадающем списке модели, при нажатии на кнопки, должны появляться соответствующие представления, которые через нужного представителя связаны с моделью.

Application

Если долго, то мне в голову пришел следующий способ. Далее много кода, но он простой.

Реализуем модели.

class IModel1(ABC):

    @abstractmethod
    def do_a(self, param):
       pass

class ConcreteModel1(IModel1):

    def do_a(self, param):
        ...


class IModel2(ABC):

    @abstractmethod
    def do_b(self, param):
        pass

class ConcreteModel2(IModel2):

    def do_b(self, param):
        ...

Реализуем представления. (View3 и View4 опущены).

class View(ABC):
    
    @abstractmethod
    def show(self):
        pass


class IView1(View):

    @abstractmethod
    def get_a(self):
        pass


class ConcreteView1(IView1):

    def get_a(self):
        ...


class IView2(View):

    @abstractmethod
    def get_b(self):
        pass


class ConcreteView2(IView2):

    def get_b(self):
        ...

Реализуем представителей. (Presenter3 и Presenter4 опущены).

class Presenter(ABC):

    @abstractmethod
    def bind_view(self, view):
        pass

    @abstractmethod
    def bind_model(self, model):
        pass

class IPresenter1(Presenter):
    
    @abstractmethod
    def do_stuff_a(self):
        pass

class ConcretePresenter1(IPresenter1):
    
    def bind_view(self, view: IView1):
        self._view = view
        #Also connect self.do_work_a to View1 button or smth ...
    
    def bind_model(self, model: IModel1):
        self._model = model
    
    def do_work_a(self):
        self._model.do_a(self._view.get_a())


class IPresenter2(Presenter):

    @abstractmethod
    def do_stuff_b(self):
        pass

class ConcretePresenter2(IPresenter2):
    
    def bind_view(self, view: IView2):
        self._view = view
        #Also connect self.do_work_b to View2 button or smth ...
    
    def bind_model(self, model: IModel2):
        self._model = model
    
    def do_work_b(self):
        self._model.do_b(self._view.get_b())

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

class MainWindow(View):

   def set_sub_views_factory(sub_views_fac):
       self._btn1_view = sub_views_fac.create_btn1_view()
       self._btn2_view = sub_views_fac.create_btn2_view()
   
   def get_btn1_view(self):
       return self._btn1_view

   def get_btn2_view(self):
       return self._btn2_view

   #Button 1 callback
   def show_btn1_view:
       self._btn1_view.show()

   #Button 2 callback
   def show_btn2_view:
       self._btn2_view.show()
   
   def get_selected_model(self):
       return 'Model name from drop down list'

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

При изменении модели в выпадающем списке, мы извлекаем из фабрики компонент нужные фабрики и обновляем все компоненты.

class MainPresenter:

   def __init__(self, main_window, components_fac):
       self._main_window = main_window
       self._components_fac = components_fac
       self._update_components()

   # Callback for drop list (if it changes it's state)
   def update_components(self):
       model = self._main_window.get_selected_model()

       sub_views_fac = self.components_fac.get_sub_views_fac(model)
       self._main_window.set_sub_views_factory(sub_views_fac)
       
       sub_presenters_fac = self.components_fac.get_sub_pres_fac(model)
       model = self.components_fac.get_model(model)
       self.bind_components(sub_presenters_fac, model) 

    def bind_components(self, sub_pres_fac, model):
        self._btn1_presenter = sub_presenters_fac.get_btn1_pres()
        self._btn1_presenter.bind_view(self._main_window.get_btn1_view())
        self._btn1_presenter.bind_model(model)

        self._btn2_presenter = sub_presenters_fac.get_btn2_pres()
        self._btn2_presenter.bind_view(self._main_window.get_btn2_view())
        self._btn2_presenter.bind_model(model)

Реализация фабрики компонент приложения.

class ComponentsFac:
  
  models_views = {
      'Model1': Model1SubViewsFac
      'Model2': Model2SubViewsFac
  }
  
  models_presenters = {
      'Model1': Model1SubPresFac
      'Model2': Model2SubPresFac
  }
  
  models = {
      'Model1': Model1
      'Model2': Model2
  }
  
  def get_sub_views_fac(model):
      return models_views[model]()
  
  def get_sub_pres_fac(model):
      return models_presenters[model]()
  
  def get_model(model):
      return models[model]()

Конкретные фабрики представлений и представителей, возвращают соответствующие (совместимые объекты).

# Concrete realization returns required views
class ISubViewsFac(ABC):
 
    @abstractmethod
    def get_btn1_view(self):
        # Concrete realization for Model1 returns View1
        # Concrete realization for Model2 returns View2

    @abstractmethod
    def get_btn2_view(self):
        # Concrete realization for Model1 returns View3
        # Concrete realization for Model2 returns View4


# Concrete realization returns required presenters 
class ISubPresFac(ABC):

    @abstractmethod
    def get_btn1_pres(self):
        # Concrete realization for Model1 returns Presenter1
        # Concrete realization for Model2 returns Presenter2

    @abstractmethod
    def get_btn2_pres(self):
        # Concrete realization for Model1 returns Presenter3
        # Concrete realization for Model2 returns Presenter4

def main():
    components_fac = ComponentsFac()
    main_window = MainWindow()
    main_presenter = MainPresenter(main_window, components_fac)
    main_window.show()

Спасибо, если осилили это. У меня ощущение, что я нагородил тут фабрик фабрик фабрик... Может кому то приходилось реализовывать подобное?


Ответы (0 шт):