开发者

Preferred style (delegates over abstract Classes or interfaces)

开发者 https://www.devze.com 2023-01-02 08:31 出处:网络
I have a utility class library I am writing.A have written a factory class that is generic enough that with a few delegates passed in from the caller it can be 开发者_如何学JAVAreused in many differen

I have a utility class library I am writing. A have written a factory class that is generic enough that with a few delegates passed in from the caller it can be 开发者_如何学JAVAreused in many different situations. However, one of my colleagues suggested that I use an abstract class or an interface so it is more explicit what functions needed to be overridden. In the model I have created to use the library you would then need to override 2 classes instead of just one. What criteria determines when it is appropriate to use delegate functions or interfaces.

Using a delegate pattern.


Class A 
{
    Func<string , ReultClass> fun1 {get;set;}

    FactoryObj CreateObj()
    {
         return fun1("")
    }
}

Using an interface pattern

Class B 
{
    InterfaceObj{get;set;}

    FactoryObj CreateObj()
    {
         return InterfaceObj.fun1("")
    }
}


If the design is such that the class depends on another class (even a simple class), I would recommend the interface approach, since the object does have to depend on outside code:

interface IA
{
    ResultClass Fun1(string arg);
}

In this case, you can still use delegates; I usually write my "anonymous" classes (as Microsoft calls them) with explicit interface implementations, so the name of the delegate property is the same as the name of the interface method it implements:

class AnonymousA : IA
{ 
    Func<string, ResultClass> Fun1 { get; set; }
    ResultClass IA.Fun1(string arg) { return this.Fun1(arg); }
} 

If there are generics involved (e.g., IA<ResultClass> and the corresponding AnonymousA<ResultClass>), then it's useful to define a factory class, just to clean up the creation syntax:

static class Anonymous
{
    public static IA<ResultClass> A<ResultClass>(Func<string, ResultClass> fun1)
    { return new AnonymousA<ResultClass> { Fun1 = fun1 }
}


I would think you would want to change the signature of the method. Instead of making the delegate a property on your class, make that a parameter you need to pass in. Then, you can't even invoke your CreateObj method without passing in the required data, and the signature itself tells you what you need to pass for the method call to be successful.


does/should the factory know about the various 'concrete' versions? Are there a fixed, relatively low number of specific concrete versions? If yes for either, IMHO the abstract base is a good approach. If the caller should be able to pass in 'arbitrary' code, you could either pass in delegates, or if multiple callers may want to handle/interpret the same data, expose it as events from the base and let the various callers hook up and do their interpretation.

Unfortunately, as SLaks mentions, it kind of depends on the specific situation - it's hard to make any kind of blanket statements - even the above one is likely to be a bad idea in some situations :)


Using wrapper classes, it's possible to do anything that can be done with delegates using interfaces, and vice versa. To determine which is appropriate for a given situation, examine the common usage patterns.

For example, suppose one wants to construct at run-time a list of actions that will need to be performed when a certain object is disposed. The most common action will be to call Dispose on an object of type IDisposable, but in some cases it may be necessary to do something else (e.g. unsubscribe an event handler).

One approach would be to maintain a list of MethodInvoker delegates and invoke each in turn(*). If cleanup will require calling Dispose on an object, make a new delegate for thatObject.Dispose() and add it to the list. An alternative approach would be to keep a list of IDisposable objects, and handle other actions by creating a class called InvokeOnDispose which holds a MethodInvoker, and invokes it when Dispose is called.

The former approach would require creating a new delegate for each cleanup action, whether it simply involved calling Dispose on something, or did something else. The latter approach would not require creating any new object instances when adding an IDisposable object to the list, but adding some other action would require creating an InvokeOnDispose object in addition to a delegate.

(*) One could simply use a MulticastDelegate rather than a list of delegates, but in some circumstances (like object cleanup) an exception thrown from one cleanup operation should probably not disrupt the operation of others. While one could assemble the collection of cleanup operations using a MulticastDelegate and then use GetInvocationList to run each within a separate 'try-catch' block, it would be easier and more efficient to simply use a list of delegates from the get-go.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号