4
$\begingroup$

I've been teaching Objects for years now as part of AP Computer Science A (which means that I am teaching OOP in Java), and I have experimented with putting Interfaces in at different locations. In general, I currently teach in the following order:

  • Class Structure: the coupling of data with relevant methods.
  • Information Hiding: making each class responsible to guarantee the integrity of its data.
  • Designing the API: Given what we've learned, how should we design the public face of our classes? Included in this section is the concept of overloading.
  • Inheritance: Creating relationships among classes, inheriting and overriding.
  • Polymorphism: Being able to utilize children in place of their parents, and how that works.

Interfaces seem most related to Polymorphism to me, since we can (for instance) make an ArrayList<SomeInterface> but wherever I put it in, it always feels like some add-on instead of like the core and vital concept that it really is.

Where (and how) do you integrate Interfaces into your OOP exposition?

$\endgroup$
8
$\begingroup$

Ideally, interfaces come first. Before Classes. Interfaces define concepts. Classes implement those concepts. Don't think of them as an add-on to OO programming. Think of them as the essence.

In fact, if you present a pre built class to students, my guess is that you already, probably informally, give the interface first. Here is a class with public methods "accept" and "spitOut", or something like that. You probably talk about the public methods first and the necessary parameters and return values. More importantly, you probably talk about the intent of the class and of the individual methods. Most likely, and I think most common, only then do you discuss the implementation of the methods along with fields, etc.

If you start your discussion with the fields, on the other hand, you are (IMO) making a big mistake and that students will start out by thinking at too low a level and will write their code too often in terms of getters and setters. In other words the encapsulation will be inconsequential and possibly leaky. Then, in a program with a few tens of classes and a few hundreds of objects, but, perhaps thousands of fields, they will be thinking about the fields and how they mutate, rather than the concepts represented by the objects in the system. Their thinking will be at a lower level than is efficient (or even possible, in some complex programs).

But if you introduce the concepts first, and get them to (a) think in terms of those concepts and (b) implement those concepts faithfully they will be naturally led to think at a higher level of abstraction, which is really what programming languages are for in the first place.

Some rules that I'd adhere to with rare exceptions and no exceptions at the start.

  1. When you implement an interface don't give the class any additional public methods. Thoroughly document the intention of the interface and its methods.
  2. When you build a subclass of an existing concrete class, don't give the subclass any public methods that don't have signatures in the superclass. (Abstract classes can be a bit different, but it is dangerous and adding public methods should be delayed.)
  3. If you need new things in a subclass, extend the interface instead, as it represents a new concept, and implement that, rather than just extending the superclass, though you can also extend as an implementation strategy.
  4. Prefer composition of objects over inheritance. An object is composed of other simpler objects (representing their own concepts). With composition you will probably be able to

    a. Write simple classes (a few public methods)
    b. having simple methods. I get antsy when a method is more than about 4 lines or has any nested internal structure.

  5. Use simple design patterns to make it fit together. These are just another sort of concept and are themselves normally defined with interfaces.
  6. Avoid getters and setters (in interfaces and classes). They lead the student to think of everything in terms of mutations of implementation, rather than at a high level. I use accessor for a method that returns information (possibly a field's value, but not always or often), and mutator for a method that somehow changes the internal state of an object. A mutator might in fact have nothing to do with fields and might pass on a message to one of the implementing objects or set up a chain of mutations. Thinking in terms of setters is too narrow, but also too low level.

Some modern OO languages don't have formal interfaces, but you can normally simulate them to separate out the ideas from the implementations.


Note that the Standard Template Library (which I haven't used in quite a while) was, itself defined in terms of concepts and the term was (maybe still is) used in the literature. This was just a placeholder for interfaces that C++ didn't support.

The idea here is to build a software system in terms of readily understood concepts. The resulting system has its complexity in the interactions of simply understood things, rather than in the things themselves.


Note that I often write an interface when I intend only one implementing class. Capture the concept first.

Again, note that I often write an abstract class that has protocol (abstract methods) for every method in any intended subclass. This lets me capture a common implementation, but keeps my concept clear as I subclass. I never need to ask instanceof and I never need to cast an object reference. Everything naturally fits together freeing my mind to think of other (higher level) things.

$\endgroup$
  • 2
    $\begingroup$ I would not expect a student to be able to implement an interface, before they have done classes, and inheritance. But they should be able to inherit an interface that you have made, and implement it. It would also be better to have a full interface (that is one with contracts). This way the student knows what to implement. $\endgroup$ – ctrl-alt-delor Dec 23 '17 at 12:37
  • $\begingroup$ I like that you think in abstract first before coding, but I am surprised that you did not mention use of a different paradigm when programming, e.g. functional or logic. I personally prefer logic, e.g. Prolog or Mercury, or other more abstract means of computation. The problem is that to much of the world is done with the old technology and sometimes using that for their exiting libraries outweighs the value in using another paradigm, e.g. NumPy $\endgroup$ – Guy Coder Dec 24 '17 at 15:41
  • $\begingroup$ While interfaces are generally developed before the implementing class; that doesn't mean you have to teach in in the same order. Interfaces are built with a consideration to the future (the implementation), which means the student needs to know what implementation entails. Interfaces are the solution to a problem, and therefore you must first showcase the problem to justify using the interface as a solution. $\endgroup$ – Flater Feb 2 '18 at 15:06
  • $\begingroup$ True enough, but interfaces are also good in the thinking stage of problem solving when you are deciding what it is you need. They are like a rough sketch of the capabilities needed. $\endgroup$ – Buffy Feb 2 '18 at 15:29
  • $\begingroup$ I agree with @Buffy here, but I would add that the idea of 'Interfaces' be introduced first as opposed to being necessarily taught in any order. It is very important for OO students to focus on the behaviors of objects. You are not required to actually use the terms when you teach the topics. ;) $\endgroup$ – Mr Bradley Feb 8 '18 at 16:41
2
$\begingroup$

I'd say that it can fit very well between the Inheritance and the Polymorphism.

Interfaces are a way of promising that any implementing class supports the interface's functionality. for example, given an interface Movable:

public interface Movable {

    public void move();

}

one knows that anything that for any object instance of a class which implements Movable, we can call .move().

This means that, unlike extending a class, implements means (and directly implies) that instances of the class have some functionality.

Teaching it in this order gives the view of an interface being nothing more than a promise for some functionality, and how such a thing is integrated into OOP.

This also avoids some of the confusion I've witnessed in cases where abstract classes are taught before interfaces. They are not the same, but understanding why it's ok for something not to have a method body (i.e. abstract methods in such a class) is clearer to students if they know interfaces.

Additionally, if one teaches interfaces as a contract of functionality, then it doesn't appear as an add-on. It's a fully-fledged OOP concept with incredible usefulness.

Whenever a person needs to describe just the functionality required by an unknown set of objects (and maybe of different classes), an interface is their best friend.

$\endgroup$
1
$\begingroup$

Interfaces should be taught right after, or during the section on APIs, because they are simply a promise to implement a certain Application Programming Interface. It's why they are called interfaces in the first place. They are also more general than inheritance, which makes them a much better example as a polymorphic data type. With this in mind, I propose that after APIs, the following concepts are taught in order:

  1. Interfaces: This is language support for what was taught during APIs, and introduces the 'duck principle'. That is, if it walks like a duck, quacks like a duck, and behaves like a duck, its a duck. This is a good argument and explanation for the need for polymorphism.
  2. Polymorphism: What good is an interface if we can't swap out one implementation for another? Take for example, Java's List interface, and two of its realizations: LinkedList and ArrayList; without polymorphism, we can't write generic code that targets both of these types.
  3. Abstract Classes: Sometimes, when writing two different realizations of the same interface, we see that code can be shared between different types, while fundamental portions of the implementation remain separated. Take, for example, designing a HumanPlayerand ComputerPlayer for a card game. Functionality relating to scoring and storing cards will be shared, but the act of choosing which card to play is fundamentally different, and cannot be shared. The interface, however, needs to be consistent.
  4. Inheritance: This is primarily for code reuse. Additional functionality or desired traits are added to a type that already is an object in its own right. Taking advantage of a somewhat nebulous implementation (such as extending Java's Exception class) is another use case. You are customizing more than you are providing functionality. Unlike the other strategies listed, you don't have to use inheritance to implement an API. You could, for example, convert a regular binary tree into a balancing binary tree.

Teaching these concepts in this order should help to showcase the use cases for each of these features. Interfaces were brushed over when I was learning programming, and it took me a while to understand what their use case was; I would try to use inheritance anywhere I needed polymorphism, which doesn't make sense in the grand majority of cases. This teaching order should get over that misconception.

$\endgroup$
1
$\begingroup$

My laundry list:

  1. We write sample programs using components from a library (say JavaFx, or SFML)
  2. We don't care (much) about the internals of objects, as long as they work, but about their programming interface = what we can do with them. Abstraction.
  3. Anyway, we're going to build new objects. Some explanations about classes/methods/fields. Encapsulation.
  4. Some details are better kept private. Enforcement of abstraction.
  5. Objects are meant to be used through their API, so we first have to design the API. How we want innocent programmers to use them. Objects as service providers.
  6. Enter the notion of (java) interface (sorry, C++ guys) as "contract", and classes realizing them.
  7. Some examples of classes with the same interface. Polymorphism.
  8. Some classes have lots in common, introduce abstract class to factorize code, and inheritance.
  9. designing abstract classes in order to be a sound basis for concrete classes (some patterns).
  10. sometimes, inheritance from a concrete class.

Students are exposed to polymorphism at a early stage: the example is a "guess a random number" game. The game is parameterized (?) by a Generator. During debugging phases a FakeGenerator is used, which always returns the same value.

public interface Generator {
   int getNumber();
}

public class FakeGenerator  implements Generator {
   final int fixedValue;
   public FakeGenerator(int value) {
       fixedValue = value;
   }
   @Override
   public int getNumber() {
       return fixedValue;
   }
}
class Game {
    Generator generator;
    Game(Generator generator) {
         this.generator = generator;
    }
...
}
Game game = new Game( 
                   // new UniformGenerator(1,10) // 
                      new FakeGenerator(33)  // for debugging
                    );
$\endgroup$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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