Is there an advantage to prohibiting the implementation of an interface for existing classes?

In OOP static languages, interfaces are used to declare that several classes have some logical property - they are one-time, they can be compared with int , they can be serialized, etc.

Say .net did not have a standard IDisposable interface, and I just came up with this great idea:

 interface IDiscardable { void Discard(); } 

My application has a lot of System.Windows.Form s, and I think that a Form satisfies the logical requirements for IDiscardable . The problem is that Form is defined outside of my project, so C# (and Java , C++ ...) will not let me implement IDiscardable for it. C# does not allow me to formally imagine the fact that a Form can be discarded (and I would probably finish the MyForm wrapper MyForm or something else.

In contrast, Haskell has typeclasses that are logically similar to interfaces. A Show instance can be represented (or serialized) as a string, Eq allows you to compare, etc. But there is one important difference: you can write an instance of typeclass (which is similar to an interface implementation) without access to the source code of the type. Therefore, if Haskell provides me with some type of Form , the record of the Discardable instance is Discardable for it.

My question is: from the point of view of the language designer, is there any advantage for the first approach? Haskell not an object-oriented language - does the second approach violate OOP ?

Thanks!

+7
c # oop programming-languages interface haskell
source share
4 answers

This is a complex issue that is associated with a general misunderstanding. Classes like Haskell (TC) are called "logically similar" to interfaces or abstract classes (IAC) from object-oriented programming languages. They are not. They represent different concepts about types and programming languages: IAC is a case of subtyping, and TC is a form of parametric polymorphism.

Nevertheless, since your questions are methodological in nature, here I answer from the methodological side. To start with the second question:

does the second approach [extending the implementation of the class outside the class] in any way violate OOP

Object-oriented programming is a set of ideas for describing the execution of a program, the basic elements of execution, how to specify these elements in the program code, and how to structure the program to separate the specification of the various elements. In particular, OOP is based on these ideas:

  • In any state of its execution, a process (executing program) consists of a set of objects. This set is dynamic: it can contain different objects in different states by creating and destroying objects.
  • Each object has an internal state represented by a set of fields, which may include links to other related objects. Relations are dynamic: the same field of the same object a can in different states point to different objects.
  • Each object can receive messages from another object. After receiving a message, an object can change its state and can send messages to objects in its fields.
  • Each object is an instance of a class : the class describes which fields an object has, what messages it can receive, and what it does when it receives a message.
  • In object a the same field af can, in different states, point to different objects that may belong to different classes. Thus, a does not need to know to which class those objects b belong; he only needs to know what messages these objects receive. For this reason, the type of these fields may be an interface . The interface declares a set of messages that an object can receive. The class explicitly indicates which interfaces are satisfied by the objects of this class.

My answer to the question: in my opinion, yes.

Implementing an interface (as suggested in the example) outside the class violates one of these ideas: the class of the object describes the complete set of messages that objects of this class can receive.

You may like to know that this is (in part) what the “Aspects”, as in AspectJ, are. The aspect describes the implementation of a particular “method” in several classes, and these implementations are incorporated (woven) into the class.

To answer the first question: “is there any advantage for the first approach”, the answer will also be yes: all the behavior of the object (what messages it answers) is described only in one place, in the class.

+6
source share

Well, the Haskell approach has one drawback that occurs when you write, for example, two different libraries, each of which provides its own implementation of the Foo interface for the same external type (provided by another third library). In this case, now these two libraries cannot be used simultaneously in the same program. Therefore, if you consider the disadvantage of a disadvantage an advantage, then I think that this would be one of the advantages for the OOP language for this, but this is a rather weak advantage.

However, I would add that classes like Haskell are a bit like OOP interfaces, but not quite like them. But type classes are also a bit like strategy templates and templates; a type type can be modeled by explicitly passing around an "dictionary" an object that provides implementations for class type operations. So, the following class is of type Haskell:

 class Monoid m where mempty :: m mappend :: m -> m -> m 

... can be modeled using this explicit dictionary type:

 data Monoid_ m = Monoid_ { _mempty :: m, _mappend :: m -> m -> m } 

... or OOP interface:

 interface Monoid<M> { M empty(); M append(M a, M b); } 

What class classes add on top of this is that the compiler will support and skip your dictionaries implicitly. Sometimes in the Haskell community, you get arguments about when class classes also outperform explicit dictionary transitions; see, for example, Gabriel Gonzalez "Discard your class types" on the blog (and keep in mind that he does not 100% agree with what he says there!). Thus, an OOP analog of this idea would be instead of expanding the language to allow external declarations of implements , what are the disadvantages of using strategies or templates only?

+5
source share

What you are describing is an adapter template. The act of compiling an object in a new type, which provides some additional behavior for the base type, in this case, the implementation of another interface.

As with many design patterns, different languages ​​choose different design patterns to directly include them in the language itself and provide special language support, often in the form of more concise syntax, while other patterns must be implemented using other mechanisms without their own special syntax.

C # does not have special language support for the adapter template, you need to create a new explicit type that will compose your other type, implement the interface and use the composed type to execute the interface contract. Is it possible that they add such a feature to the language, of course. Like any other function request, it must be designed, implemented, tested, documented and all sorts of other expenses. This function (so far) has not made a cut.

+2
source share

What you describe is called a duck, after the phrase "If she walks like a duck, swims like a duck, and quacks like a duck and then a duck."

C # actually allows you to use a dynamic (run-time) duck using the dynamic keyword. What it does not allow is a static (compilation) duck type.

You probably need someone from Microsoft to come and point out the exact reasons it doesn't exist in C #, but here are some likely candidates:

  • minus 100 pips to add features. This is not just so that the function does not have flaws, to justify the efforts to implement, test, support and support the language function, it should provide a clear advantage. There are not many situations between the dynamic keyword and the adapter template when this is useful. The reflection is also powerful enough that duck printing can be effectively provided, for example, I believe that it would be quite simple to use Castle DynamicProxy for this.

  • There are situations when you want a class to be able to indicate how it is available. For example, fluent APIs often control the actual orders and combinations of chain methods in a class using interfaces. See, for example, in this article . If my free class were designed around a grammar that said that when method A was called, methods other than B could not be called, I could control this using interfaces such as:

     public class FluentExample : ICanCallAB { public ICanCallB A() { return this; } public ICanCallAB B() { return this; } } public interface ICanCallA { void A(); } public interface ICanCallAB : ICanCallA { void B(); } 

    Of course, the consumer can get around this with casting or dynamic , but at least in this case the class can indicate its intention.

  • In connection with the foregoing, the implementation of an interface is a declaration of meaning. For example, Tree and Poodle may have a Bark() element, but I would like to be able to use Tree as an IDog .

+1
source share

All Articles