5

I'm trying to understand OOP so I can write better OOP code and one thing which keeps coming up is this concept of a delegate (using .NET). I could have an object, which is totally self contained (encapsulated); it knows nothing of the outside world... but then I attach a delegate to it.

In my head, this is still quite well separated as the delegate only knows what to reference, but this by itself means it has to know about something else outside it's world! That a method exists within another class!

Have I got myself it total muddle here, or is this a grey area, or is this actually down to interpretation (and if so, sorry as that will be off topic I'm sure). My question is, do delegates defy/muddy the OOP pattern?

3
  • 3
    Might I submit that not everything about your design needs to be strict, by the books OOP?
    – KChaloux
    Commented Aug 2, 2013 at 13:03
  • 2
    It is about message passing. The caller does not need to know which class the object belongs to, or what interface it implements. All the caller knows is that something accepts this type of message.
    – rwong
    Commented Aug 2, 2013 at 13:11
  • It is sometimes said that OOP patterns were invented to work around the restrictiveness of some programming languages. Put in another way, the OOP patterns would become transparent when used in some higher-level language (e.g. functional programming). When the restrictiveness goes away, e.g. by way of delegates, one may find patterns less visible, but it is still OOP.
    – rwong
    Commented Aug 2, 2013 at 13:13

6 Answers 6

7

In my head, this is still quite well separated as the delegate only knows what to reference, but this by itself means it has to know about something else outside it's world!

But it doesn't. Your delegate is just a variable that when invoked calls something. If you have a string variable, it could contain anything. Your class isn't somehow breaking encapsulation if another class sets your string variable to "foobar". Likewise, your class' encapsulation is not broken if your delegate is set to "hide this UI widget when triggered".

3

If OOP means each object is responsible for its own interface, then logically delegates could break OOP if it were not used properly. For example, if I made a FileStream class which, when asked to write, called a delegate to perform the write on behalf of the FileStream class, then it would breach OOP. This is because the responsibility of the write operation of the FileStream is purely upon FileStream if we're following OOP to the letter.

However, this doesn't mean that using delegates always goes against OOP. Using the same example, if I used a delegate to handle events before and after writes, this is still following OOP because it is not the responsibility of FileStream to know what should happen before and after writes. It should merely notify the classes which are responsible.

If passing delegates to an object were breaching OOP because it knows something outside its world, then also passing variables would breach OOP since you could think of delegates as passing code rather than data to classes. Acknowledging that there exists code outside of a single OOP class is not breaching anything.

3

No. Encapsulation doesn't insist that an object "not know anything about the outside world." It generally says two things:

  • Other objects don't need to know how an objects does what it says it does.
  • All methods and attributes related only to serving a particular class are contained in that class.

The second point is often stated in various manners that can gives an impression that objects shouldn't rely on outside stuff; but this is a highly impractical understanding, if not an impossible one. Consider a really basic class:

class Document {
  public String Title;
  public String Body;
}

Even without any further definition, our Document class already knows about "outside" things: It knows about Strings! (String is an externally defined class.) And, ignoring the potential XSS issues for a moment, we might make a method that looks like this:

public String GetHtmlBlurb() {
  return "<div><b>" + Title.Substring(0, 60) + "</b> "
    + body.Substring(0, 120) + "</div>";
}

The Substring method doesn't exist in our Document definition either! It's an externally defined method in the String class! But, this is perfectly expected, acceptable, generally necessary OO code. With the exception of classes built exclusively around primitives (do primitives really even exist in .NET?), a class is defined in terms of other classes (external stuff, the outside world, etc.).

Regarding delegates, we could have similarly defined our Document to work with a variety of Body types we have no other information about. All we might require is some way of producing a substring wherein there are multiple ways of doing so (perhaps one version adds elipses in certain cases). All our Document cares about is the ability to call some method that claims to be a Subtring-like method.

class Document {
  public String Title;
  public Func<Int32, Int32, String> BodySubstring;

  public String GetHtmlBlurb() {
    return "<div><b>" + Title.Substring(0, 60) + "</b> "
      + BodySubstring(0, 120) + "</div>";
  }
}

In brief: A delegate is just a reference to a method, rather than the full object we might otherwise include as a property. The difference is that our delegate doesn't require the object to be of a certain type: only that a particular method have a certain signature. Our class doesn't know what the object does and doesn't insist on knowing anything about it. So, in that respect, it actually knows less about "the outside world."

Perhaps it's not an infringement on encapsulation. Perhaps it's uberencapsulation.

1
  • Bear in mind, my examples here are silly -- a silly, useless class and a silly reason to use a delegate.
    – svidgen
    Commented Aug 7, 2013 at 16:04
1

A delegate is just another piece of data, an invokable bit of data, true, but still just data. It can be passed to another object, or exposed to and then changed by another object. Your object does not necessarily know about another object -- your object could have the delegate set to a method inside itself.

You as the programmer know more, but the class doesn't have to. Delegates make methods into data, and that is a good thing...

1

But what does it really know? By passing in a delegate, such as a callback, event handler, factory method, etc, all that is known by the code that gets this delegate reference is that it has a reference to something it can or should call at a specific time, or for a specific purpose. It doesn't have to know exactly what that call will do (outside of any return value expected), where the actual code that will run lives, how it was chosen to be used as a delegate, etc. It needs no knowledge about this delegate other than it has it, and knows how to call it, which are the minimum requirements (in C# at least).

The delegate definition is therefore little more than a mini-interface; you define a delegate type, possibly named as to its ostensible purpose in consuming code, that expects a certain number of named parameters and is expected in turn to return a particular value. This is simplified even further with .NET 3.5's built-in generic delegate types like Action, Func, Predicate, Comparison etc; you don't even have to define your own strongly-typed delegate, just ask for a Func<string, string> (the downside being that general-purpose generics are, well, general-purpose and generic; you can't use the delegate type name or parameter names to self-document what the delegate is expected to do for the calling code, or what types of things you should give it).

It's similar, then, to a full interface definition, which defines a list of such methods, by name, with fully-defined signatures, all of which must be available in an implementing class. In Java, this is more or less how the same functionality you get with .NET delegates is achieved; an interface is defined with a single method representing the delegate method, classes exposing this method for use as a delegate are marked as implementing the interface, and a reference to the class is given, on which the method can be called. Delegates simply remove the middleman; you no longer care that the class containing the method implements any defined interface, you care that the actual method in question matches the required signature (here, signature is just parameter list and return type, not including method name). The class containing that method could then have a dozen methods with the same parameters and return type, but different names, and some machination your code doesn't have to care about can choose a specific one to give to you as a delegate.

Understand that while C# is considered an object-oriented language, it is also a multi-paradigm language, incorporating procedural and functional aspects of other languages into itself on request or demonstrated need. Delegates were a lesson learned by observing Java programmers and their travails of setting up simple event-driven, multi-tier applications in a strictly-enforced object-oriented paradigm, requiring hundreds of highly specific single-method interfaces. Java eventually worked around this problem by allowing anonymous interface implementations, so code that needed to inject an interface could define a one-off implementation inline, but it can still be a bit verbose. The rumor mill is also churning that Oracle has given in to popular demand, and lambda statements and other delegates will be part of the upcoming Java 8 specification (along with a lot of other syntax sugar goodness that Java devs are salivating over after looking across the fence to the C# side).

C++ avoided that by retaining the highly pointer-based flexibility of the C lineage, but its limitation was that, unlike interfaces, function pointers couldn't self-document; all you could know, without external documentation or traversing source code, is that you need to pass the address of a function, but you couldn't know anything about what parameters the consumer would try to pass, what it might expect as a return value, what your function might be expected to do and when it might be called, etc. As the ability to write self-documenting code was a key tenet of C#'s language spec, the designers combined the two, by allowing function pointers in a sense, but requiring a strong definition of the method's I/O signature as a type, allowing compiler-checked, self-describing function pointers without needing full interfaces.

1
  • sorry, but you can specify a function prototype in C++ (or C) to strongly type a function pointer. For example
    – gbjbaanb
    Commented Sep 18, 2013 at 11:48
1

No. Delegate complements OOP.

Because OOP has no notion of having function/method as data to be passed as event handler value, and for callbacks. Also having method as data therefore can be included as a parameter in the method, gaves us expressiveness and flexibility.

The necessity becomes more if you get introduced to code asynchronously, since you need to know which function/method as a callback to do something after your code returns continuations.

Therefore, please don't limit yourself to the definition of OOP as is. Those established OOP languages such as C#, VB, F#, even C++ has supports for delegates, otherwise these languages will lose competitiveness if there are no delegate supports at all.

Of course if you're implementing delegates in a bad way such as having bad smell in your code, the net result is the same as implementing OOP badly.

Your Answer

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

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