Extension interface patterns

后端 未结 11 1996
失恋的感觉
失恋的感觉 2020-12-14 16:56

The new extensions in .Net 3.5 allow functionality to be split out from interfaces.

For instance in .Net 2.0

public interface IHaveChildren {
    str         


        
相关标签:
11条回答
  • 2020-12-14 17:10

    I see separating the domain/model and UI/view functionality using extension methods as a good thing, especially since they can reside in separate namespaces.

    For example:

    namespace Model
    {
        class Person
        {
            public string Title { get; set; }
            public string FirstName { get; set; }
            public string Surname { get; set; }
        }
    }
    
    namespace View
    {
        static class PersonExtensions
        {
            public static string FullName(this Model.Person p)
            {
                return p.Title + " " + p.FirstName + " " + p.Surname;
            }
    
            public static string FormalName(this Model.Person p)
            {
                return p.Title + " " + p.FirstName[0] + ". " + p.Surname;
            }
        }
    }
    

    This way extension methods can be used similarly to XAML data templates. You can't access private/protected members of the class but it allows the data abstraction to be maintained without excessive code duplication throughout the application.

    0 讨论(0)
  • 2020-12-14 17:10

    Rob Connery (Subsonic and MVC Storefront) implemented an IRepository-like pattern in his Storefront application. It's not quite the pattern above, but it does share some similarities.

    The data layer returns IQueryable which permits the consuming layer to apply filtering and sorting expression on top of that. The bonus is being able to specify a single GetProducts method, for example, and then decide appropriately in the consuming layer how you want that sorting, filtering or even just a particular range of results.

    Not a traditional approach, but very cool and definitely a case of DRY.

    0 讨论(0)
  • 2020-12-14 17:19

    I see a lot of people advocating using a base class to share common functionality. Be careful with this - you should favor composition over inheritance. Inheritance should only be used for polymorphism, when it makes sense from a modelling point of view. It is not a good tool for code reuse.

    As for the question: Be ware of the limitations when doing this - for example in the code shown, using an extension method to implement GetChildren effectively 'seals' this implementation and doesn't allow any IHaveChildren impl to provide its own if needed. If this is OK, then I dont mind the extension method approach that much. It is not set in stone, and can usually be easily refactored when more flexibility is needed later.

    For greater flexibility, using the strategy pattern may be preferable. Something like:

    public interface IHaveChildren 
    {
        string ParentType { get; }
        int ParentId { get; }
    }
    
    public interface IChildIterator
    {
        IEnumerable<IChild> GetChildren();
    }
    
    public void DefaultChildIterator : IChildIterator
    {
        private readonly IHaveChildren _parent;
    
        public DefaultChildIterator(IHaveChildren parent)
        {
            _parent = parent; 
        }
    
        public IEnumerable<IChild> GetChildren() 
        { 
            // default child iterator impl
        }
    }
    
    public class Node : IHaveChildren, IChildIterator
    { 
        // *snip*
    
        public IEnumerable<IChild> GetChildren()
        {
            return new DefaultChildIterator(this).GetChildren();
        }
    }
    
    0 讨论(0)
  • 2020-12-14 17:22

    I think the judicious use of extension methods put interfaces on a more equatable position with (abstract) base classes.


    Versioning. One advantage base classes have over interfaces is that you can easily add new virtual members in a later version, whereas adding members to an interface will break implementers built against the old version of the library. Instead, a new version of the interface with the new members needs to be created, and the library will have to work around or limit access to legacy objects only implementing the original interface.

    As a concrete example, the first version of a library might define an interface like so:

    public interface INode {
      INode Root { get; }
      List<INode> GetChildren( );
    }
    

    Once the library has released, we cannot modify the interface without breaking current users. Instead, in the next release we would need to define a new interface to add additional functionalty:

    public interface IChildNode : INode {
      INode Parent { get; }
    }
    

    However, only users of the new library will be able to implement the new interface. In order to work with legacy code, we need to adapt the old implementation, which an extension method can handle nicely:

    public static class NodeExtensions {
      public INode GetParent( this INode node ) {
        // If the node implements the new interface, call it directly.
        var childNode = node as IChildNode;
        if( !object.ReferenceEquals( childNode, null ) )
          return childNode.Parent;
    
        // Otherwise, fall back on a default implementation.
        return FindParent( node, node.Root );
      }
    }
    

    Now all users of the new library can treat both legacy and modern implementations identically.


    Overloads. Another area where extension methods can be useful is in providing overloads for interface methods. You might have a method with several parameters to control its action, of which only the first one or two are important in the 90% case. Since C# does not allow setting default values for parameters, users either have to call the fully parameterized method every time, or every implementation must implement the trivial overloads for the core method.

    Instead extension methods can be used to provide the trivial overload implementations:

    public interface ILongMethod {
      public bool LongMethod( string s, double d, int i, object o, ... );
    }
    
    ...
    public static LongMethodExtensions {
      public bool LongMethod( this ILongMethod lm, string s, double d ) {
        lm.LongMethod( s, d, 0, null );
      }
      ...
    }
    


    Please note that both of these cases are written in terms of the operations provided by the interfaces, and involve trivial or well-known default implementations. That said, you can only inherit from a class once, and the targeted use of extension methods can provide a valuable way to deal with some of the niceties provided by base classes that interfaces lack :)


    Edit: A related post by Joe Duffy: Extension methods as default interface method implementations

    0 讨论(0)
  • 2020-12-14 17:26

    A little bit more.

    If multiple interfaces have the same extension method signature, you would need to explicitly convert the caller to one interface type and then call the method. E.g.

    ((IFirst)this).AmbigousMethod()
    
    0 讨论(0)
提交回复
热议问题