Is this design pattern logical?
The following C# is abstract so you can see the structure of what I am trying to accomplish
This is the Composite (GoF) Pattern I am using to represent a FileSystem treeinterface IComponent
{
void Render();
void Add(INode);
void Remove(INode);
}
class Folder : IComponent
{
List<IComponent> filesAndFolders = new List<IComponent>();
void Render()
{
Console.WriteLine("This is a folder, num childs: " +
}
void Add(IComponent add)
{
filesAndFolders.Add(add);
}
void Remove(IComponent rem)
{
filesAndFolders.Remove(rem);
}
}
class File : IComponent
{
void Render()
{
Console.WriteLine("This is a file");
}
void Add(IComponent add)
{
//do nothing... cant add folder to file
}
void Remove() { } //same as above
}
My problem with the above code is now I have File which does not implement Add or Remove...
I could:- Remove the add and remove from Component, but then I believe I am breaking the pattern a bit.
- Use a complement pattern (decorator?) to change Add in the leaf and composite classes. For example, force Folder somehow to have a method Folder.AddFileOrFolder(Component) and File to have File.AddSibling(File).
- Look at a different pattern. Perhaps I am doing it wrong or trying to accomplish something impossible without knowing more about my requirements? For example some questions would b开发者_开发问答e how is/should the pattern I use interact with the Viewing of the objects and how the user input affects the objects.
These files and folders are really representations of objects on a remote host, they are not acctual files and folders on the hard disk. One user interaction will be when a "file" in the application is dragged onto the desktop, a file is downloaded.
Bonus (some what related) question:
What would be a good trick or technique to cache the files in my application so that if the user does interact with the "virtual" file they see the result faster.Thank You.
I would suggest simply having two classes, Folder and File. The Folder has two collections, Folders and Files. No need to complicate it with ill-fitting patterns. If Files and Folders have some common methods/properties (such as Name), you can create an appropriate interface for just the shared methods/properties.
I agree with @Brian. I would create an interface with information that exists both for files and folders.
interface IFileSystemItem
{
string Name {get; set;}
// folder for files, parent folder for folders, null for root folders.
IFileSystemItem Parent {get;set;}
DateTime CreatedAt {get;set;}
DateTime ModifiedAt {get;set;}
ISecurityInfo SecurityInfo {get;set;}
}
Don't try to use patterns without a reason, will only complicate things.
Using the composite pattern here is particularly useful if you plan on implementing the visitor pattern with it. In other words, over time you might want to add any number of unforeseen activities on your file/folder structure. For instance, you didn't know you needed the ability to inspect your filesystem for csproj files that contained a reference to EnvDTE or count the number of zero-length files. That is easy with the combination of these two patterns. Sometimes the patterns are useful. Sometimes they go down in history as "it looks like somebody was learning a pattern". Consider the larger business requirements with an engineering trade study. Particularly identify if there is a need for scalability or extensibility and make a decision.
I perceive the same problem with almost all patterns that use interfaces. The caller of an interface method has no assurance whatsoever that the implementer performs a particular task, or indeed does anything at all. This dissatisfies me, and I'm not sure I know of a pleasing resolution to this philosophical problem.
One idea I had is to expect every interface method to return an instance of a class - this class should have a private constructor and require certain steps to create it, to 'prove' that the creating method is doing something relevant. It would be like a 'report' of work done that a stranger - which an interface implementer is - can return to the caller for verification.
On the other hand, this could be considered an anti-pattern because the point of interfaces is that you don't care about the underlying implementation. This means that you have to structure your code so it's the implementer's problem if it doesn't expose the expected behaviour, not the caller's.
精彩评论