lundi 6 février 2017

What Design Pattern Have I Ended Up With?

We have a Silverlight application. It has a couple of pages that sit inside tabs in our UI. In the past, we've called them SavePage, and PanelPage. Save page just has basic functionality for editing the detail of a record, creating new records, and deleting the existing record on screen. PanelPage inherits from SavePage. PanelPage is a little more sophisticated in that panels become visible/invisible based on selections you make on screen.

The code was a huge mess in the Silverlight application. But, recently, I took the step of porting this code to work in Xamarin Forms. I made two failed attempts to do this, and on my third attempt, I got the code to work on all targeted platforms: Silverlight, iOS, Android, and Windows UWP. I'm fairly happy with the class design for now. It could be simpler, but this will do the trick for a while.

The point of this design is that the UI logic is abstracted away from the physical UI controls themselves. I removed the System.Windows usings away from the shared code that sits across both platforms (SavePage, and PanelPage). These pages are more like "Controllers" in the MVC, or MVVC patterns. But, I don't think that what I have created is exactly either of those two patterns. However, I had to split these classes up in to two parts: one for abstract UI calls like SaveAsync(), and one for platform specific UI calls like ReportError. I'm struggling to get the naming of the classes right since I don't even know what design pattern I am using.

Here is a class diagram: Class Diagram Here is the code for some of the interfaces concerned:

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;
}

These interfaces have been implemented successfully in Silverlight and Xamarin Forms. So, is this similar to another UI design pattern? Can anyone recommend improvements? Or, tell me what I'd need to do to convert this to a more standard UI design pattern? How about naming? What should I name my classes and interfaces here?

Aucun commentaire:

Enregistrer un commentaire