2
\$\begingroup\$

I have the following interface

public interface IDocumentController : IController
{
    bool Handles(string path);
    DocumentView New(string fileName);
    DocumentView Open(string path);
    //...
    string FilePath { get; set; }
}

I have an abstract base class for all IDocumentController types that implements the interface.

public abstract class DocumentController : IDocumentController
{
    private IUndoRedoManager undoRedoManager;

    public virtual void UpdateDisplayName(IDocumentView view, bool isDirty)
    {
        if (isDirty)
        {
            if (!view.DisplayName.EndsWith("*"))
                view.DisplayName += "*";
        }
        else
            view.DisplayName = Path.GetFileName(FilePath);
    }

    public virtual void ActivateView(object sender, EventArgs e)
    {
        IDocumentView view = (IDocumentView)sender;
        ((Form)view).ShowIcon = true;
        Log.Trace($"DocumentController.ActivateView(): " + 
            "View \"{view.DisplayName}\" activated");
    }

    public virtual void DeactivateView(object sender, EventArgs e)
    {
        IDocumentView view = (IDocumentView)sender;
        ((Form)view).ShowIcon = false;
        Log.Trace($"DocumentController.DeactivateView(): " +
            "View \"{view.DisplayName}\" deactivated");
    }

    public void FileChangedHandler(string p)
    {
        if (p.CompareNoCase(FilePath))
            HasChangedOnDisk = true;
    }

    public abstract string DefaultExtension { get; }

    public abstract IView View { get; }

    public virtual bool Handles(string path)
    {
        string extension = Path.GetExtension(path);
        if (!String.IsNullOrEmpty(extension))
        {
            if (FileTypes.SelectMany(ft => ft.FileExtensions)
                             .Any(e => e.CompareNoCase(extension)))
                return true;
        }
        return false;
    }

    public abstract DocumentView New(string fileName);

    public abstract DocumentView Open(string path);

    //...

    public abstract string FilePath { get; set; }

}

and a little bit more... This class contains common functionality for all documents. All is working fine, but I now want to introduce a file system watcher which gets hooked up for all IDocumentController regardless of type whenever a file is opened. Of course Open itself cannot be common as different document types required different opening methodology, so I want to "decorate" the overriden Open method in each IDocumentController class with the logic to setup the watcher. I am proposing I merely add a virtual method

private FileSystemObserver observer;

public virtual void StartFileSystemObserver()
{
    observer = new FileSystemObserver(Path.GetDirectoryName(FilePath), "*.*", false);
    observer.FileChanged += FileChangedHandler;
    observer.FileDeleted += FileChangedHandler;
    observer.Start();
}

which will have to be used by all document controllers that wish to observe changes of the opened file . So in a derived concrete class say SpreadsheetDocument I will have

public override DocumentView Open(string path)
{
    // Open my document...
    UpdateDisplayName((IDocumentView)View, false);    
}

But this will mean all documents controllers inheriting from DocumentController will have to remember to invoke StartFileSystemWatcher() in their Open method.

Is there a way (I am sure there is, but it has been a long day) to ensure that StartFileSystemObserver() is always invoked after the Open method is called for any derived DocumentController?

\$\endgroup\$
2
  • \$\begingroup\$ What varies between different implementations of IDocumentController? \$\endgroup\$ Apr 27, 2016 at 19:48
  • \$\begingroup\$ Could your base class implementation of FilePath set do this? \$\endgroup\$
    – hocho
    Apr 27, 2016 at 22:00

1 Answer 1

1
\$\begingroup\$

You can implement IDocumentController.Open() as such :

public DocumentView Open(string path)
{
    var document = OpenImpl(path);

    StartFileSystemObserver();

    return document;
}
protected abstract DocumentView OpenImpl(string path);

And have your concrete document controller to implement OpenImpl() instead.


BTW, UpdateDisplayName() can be simplified to this :

view.DisplayName = Path.GetFileName(FilePath) + (isDirty ? "*" : "");

and, Handles(), to this :

return FileTypes
    .SelectMany(ft => ft.FileExtensions)
    .Contains(Path.GetExtension(path), StringComparer.CurrentCultureIgnoreCase);
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.