2

У нас есть приложение Silverlight. В нем есть несколько страниц, которые находятся внутри вкладок в нашем интерфейсе. Раньше мы называли их SavePage и PanelPage. Сохранить страницу имеет только базовые функции для редактирования деталей записи, создания новых записей и удаления существующей записи на экране. PanelPage наследуется от SavePage. PanelPage немного сложнее в том, что панели становятся видимыми/невидимыми, основываясь на выборе, который вы делаете на экране.Какой шаблон дизайна у меня закончился?

Код был огромным беспорядком в приложении Silverlight. Но недавно я сделал шаг портирования этого кода для работы в Xamarin Forms. Я сделал две неудачные попытки сделать это, и с моей третьей попытки я получил код для работы на всех целевых платформах: Silverlight, iOS, Android и Windows UWP. На данный момент я доволен дизайном класса. Это может быть проще, но это поможет сделать какое-то время.

Целью этого дизайна является то, что логика пользовательского интерфейса абстрагируется от физических элементов управления пользовательского интерфейса. Я удалил использование System.Windows от общего кода, который находится на обеих платформах (SavePage и PanelPage). Эти страницы больше похожи на «Контроллеры» в шаблонах MVC или MVVC. Но я не думаю, что то, что я создал, - это точно один из этих двух шаблонов. Тем не менее, мне пришлось разделить эти классы на две части: одну для абстрактных пользовательских интерфейсов, таких как SaveAsync(), и одну для специфичных для платформы UI-вызовов, таких как ReportError. Я изо всех сил пытаюсь получить название классов правильно, так как я даже не знаю, какой шаблон дизайна я использую.

Вот диаграмма классов: Class Diagram Вот код для некоторых из интерфейсов заинтересованных:

public interface IPage : IRecordSelector 
{ 
    /// <summary> 
    /// This event should be raised when the busy state of a tab changes 
    /// </summary> 
    event EventHandler<BusyStateChangedEventArgs> BusyStateChanged; 
    object PageElement { get; } 
} 

public interface IButtonDrivenPage : IPage 
{ 
    Task SaveClickAsync(); 
    Task DuplicateClickAsync(); 
    Task DeleteClickAsync(); 
    Task NewClickAsync(); 
    event EventHandler<ButtonVisibilityChangedEventArgs> ButtonVisibilityChanged; 
    IRecord GetRecord(); 
} 

public interface ISavePage : IButtonDrivenPage, IRequestClose 
{ 
    string DataContextXmlSnapshot { get; } 
    bool PromptForChangeCancel { get; } 
    IRecord SelectedItem { get; } 
    Task SetSelectedItemAsync(IRecord selectedItem); 
    event EventHandler SelectedItemChanged; 
    void Close(); 
    void SetAutomationObject(object automationObject); 
    ISavePageUIController SavePageUIController { get; } 
} 

public interface ISavePageUIController: IDisposable 
{ 
    /// <summary> 
    /// The UI controller is notifying the page that the UI content has been loaded 
    /// </summary> 
    event EventHandler ContentLoaded; 

    /// <summary> 
    /// Prompt the user for a yet or a no 
    /// </summary> 
    Task<bool> GetYesNoFromPrompt(string message, string title); 

    /// <summary> 
    /// Report an error to the user 
    /// </summary> 
    void ReportError(string title, string message, Exception exception); 

    /// <summary> 
    /// Notifies the UI that the DataContext/Binding context has changed 
    /// </summary> 
    void SetSelectedItem(IRecord selectedItem); 

    /// <summary> 
    /// The actual UI object that is displayed on screen as the content of the page 
    /// </summary> 
    object PageElement { get; } 

    /// <summary> 
    /// Clears residual errors from the screen if they exist 
    /// </summary> 
    void ClearErrors(); 

    /// <summary> 
    /// The record was saved. The selectedItem parameter will be the saved record from the server. 
    /// </summary> 
    void CurrentRecordSaved(IRecord selectedItem); 

    /// <summary> 
    /// This event occurs when the UI wants to notify the controller that a Save button has been clicked in the UI somewhere 
    /// </summary> 
    event EventHandler SaveClicked; 
} 


public interface IPanelUIController : ISavePageUIController 
{ 
    void CreateAndAddPanelFromContent(PagePanel pagePanel, double? panelHeight); 
    IEnumerable<IPagePanel> GetIPagePanelControls(); 
    void SetHeader(IPanelHeader pageHeader); 
    void SetVisiblePanels(IList<bool> visiblePanels); 
    void HideAllPanels(); 
    event EventHandler<RecordsSelectedRoutedEventArgs> PanelPageRecordsSelected; 
} 

Эти интерфейсы были успешно реализованы в Silverlight и Xamarin форм. Итак, похоже ли это на другой шаблон дизайна пользовательского интерфейса? Может ли кто-нибудь рекомендовать улучшения? Или, скажите мне, что мне нужно сделать, чтобы преобразовать это в более стандартный шаблон дизайна пользовательского интерфейса? Как насчет имен? Что я должен назвать здесь своими классами и интерфейсами?

ответ

2

Если честно, я бы не был слишком одержим тем, что был тем или иным (MVC, MVVM или MVP), они почти одинаковы, и дело в том, чтобы сохранить этот «большой раскол». Тем не менее, сейчас, ИМХО, кажется, ближе всего к MVP (Model View Presenter)

Проблема заключается в том, что у вас есть много приобщены логики там: IPage должны действительно быть просто View, но вы должны это делать то, что обычно контролировал контроллер. То же, что и у его детей: ISavePage имеет метод под названием SetAutomation объект, который я обычно ожидал увидеть в Controller (по крайней мере, если я правильно угадываю его функцию).

Adam Freeman отлично поработал над тем, как разбить эту штуку в ASP.NET MVC 5: http://enos.itcollege.ee/~ijogi/Nooks/Pro%20ASP.NET%20MVC%205/Pro%20ASP.NET%20MVC%205.9781430265290.pdf Посмотрите на страницу 51, где он разбивает, что каждый элемент должен быть концептуально, что может помочь?

Я бы попытался сделать это таким образом, учитывая, что мы установили, что ваш IPage действительно является контроллером.

public class PageElement 
{ 
    IPageController _controller; 

    // these are your models - your controller will simply allow them to be shown with your other methods 
    private PageData _data; 
    private PageData _otherData; 

    public PageElement(IPageController ctrl) 
    { 
     _controller = ctrl; 
    } 
} 

public class PageController : IPageController 
{ 
    IPageService _service; 

    public PageController(IPageService service) 
    { 
      _service = service; 
    } 

    // this is what your button calls when clicked 
    public void SaveAsync(object sender, SomeEventArgs args) 
    { 
      // the service does the actual work 
      _service.SaveAsync() 
    } 
} 

public class PageService : IPageService 
{ 
    public void SaveAsync(){ // what it does} 

} 
+0

Я могу видеть ваши очки здесь. Проблема в том, что IPage на самом деле не является представлением. Свойство PageElement - это объект пользовательского интерфейса, который платформа отображает на экране, но помимо этого интерфейс IPage действительно является контроллером. Исторически это называлось IPage, но я рассматривал возможность переименования его в IPageController. –

+0

Было бы справедливо сказать, что PageElement не должен существовать на контроллере, но способ, которым работает наша система, заключается в том, что создается класс IPage, а затем добавлен элемент PageElement в Visual Tree. –

+0

Я слышу вас - вот что я хотел бы сделать в качестве примера: предполагая, что IPage является контроллером, я бы просто назвал его IPageController. Он должен ограничиваться управлением элементами страницы и обновлением данных из службы. Эта служба должна быть отдельным объектом, который фактически работает. Таким образом, у вас будет элемент PageElement, содержащий элемент управления IPage в качестве члена. После этого IPageController будет иметь сервис (IPageService), так что методы SaveClickAsync действительно просто вызовут какой-то метод в вашем сервисе, чтобы справляться с сохранением и вещами такого характера. – prestonsmith

0

Посмотрите, как MVC, или предыдущий шаблон контроллера представления (до MVC)