Explaining Interfaces to Students [closed]

2020-01-26 23:52发布

For a few years I was a teaching assistant for an introduction to programming module - Java for first year undergraduates.

Mostly it went well and we managed to get object-oriented programming across to the students quite well, but one thing that students rarely saw the point of was interfaces.

Pretty much any explanation we gave either came across as too contrived to be useful for learning, or too far removed from their position as beginners. The reaction we tended to get was "I... see," translated as "I don't understand and they don't sound useful".

Anyone here have a way of successfully teaching students about interfaces? I'm not a teaching assistant any more, but it's always nagged at me.

26条回答
Evening l夕情丶
2楼-- · 2020-01-27 00:38

The real value of interfaces comes with being able to override components in 3rd party APIs or frameworks. I would construct an assignment where the students need to override functionality in a pre-built library that they cannot change (and do not have the source for).

To be more concrete, let's say you have a "framework" that generates an HTML page implemented as a Page class. And page.render(stream) generates the html. Let's say that Page takes an instance of the sealed ButtonTemplate class. The ButtonTemplate object has its own render method so that in page.render(stream) buttonTemplate.render(label,stream) gets called anywhere there is a button and it produces the html for a submit button. As an example to the students, let's say that we want to replace those submit buttons with links.

I wouldn't give them much direction other than describing the final output. They will have to pound their heads trying various solutions. "Should we try to parse out the button tags and replace with anchor tags? Can we subclass ButtonTemplate to do what we want? Oh, wait. It's sealed! What were they thinking when they sealed this class!?!" Then after that assignment show a second framework with the ILabeledTemplate interface with the render(label,stream) method.

查看更多
淡お忘
3楼-- · 2020-01-27 00:39

In addition to the other answers, you could try explaining it from a different perspective. The students I'm sure already know about inheritance because it is jammed down the throats of every Java student from probably lecture one. Have they heard about multiple inheritance? Method resolution was seen as a design issue in C++ (and also in Perl and other multiple-inheritance languages) because conceptually it's ambiguous as to exactly what should happen when a method is called in a subclass that is defined in two of its base classes. Are both executed? Which one goes first? Can one be referenced specifically? See also the diamond problem. It's my understanding that this confusion was resolved simply by introducing interfaces, which have no implementation, so there's no ambiguity as to which implementation to use during method resolution.

查看更多
冷血范
4楼-- · 2020-01-27 00:40

Interface Oriented Design describes this better than I ever could http://pragprog.com/titles/kpiod/interface-oriented-design. The author uses some excellent examples of interfaces versus inheritance for things like the taxonomy of the animal kingdom. It has some of the best arguments against excessive inheritance and judicious use of interfaces I have read to date.

A bunch of websites with incompatible ways of bringing them up:

Listing of Facebook.java:

public class Facebook {
    public void showFacebook() {
        // ...
    }
}

Listing of YouTube.java:

public class YouTube {
    public void showYouTube() {
        // ...
    }
}

Listing of StackOverflow.java:

public class StackOverflow {
    public void showStackOverflow() {
        // ...
    }
}

A client manually handling the different methods the websites use to bring themselves up:

Listing of ClientWithoutInterface.java:

public class ClientWithoutInterface {
    public static void main(String... args) {
        String websiteRequested = args[0];
        if ("facebook".equals(websiteRequested)) {
            new Facebook().showFacebook();
        } else if ("youtube".equals(websiteRequested)) {
            new YouTube().showYouTube();
        } else if ("stackoverflow".equals(websiteRequested)) {
            new StackOverflow().showStackOverflow();
        }
    }
}

Introduce a Website interface to make the client's job easier:

Listing of Website.java:

public interface Website {
    void showWebsite();
}

Listing of Facebook.java:

public class Facebook implements Website {
    public void showWebsite() {
        // ...
    }
}

Listing of YouTube.java:

public class YouTube implements Website {
    public void showWebsite() {
        // ...
    }
}

Listing of StackOverflow.java:

public class StackOverflow implements Website {
    public void showWebsite() {
        // ...
    }
}

Listing of ClientWithInterface.java:

public class ClientWithInterface {
    public static void main(String... args) {
        String websiteRequested = args[0];
        Website website;
        if ("facebook".equals(websiteRequested)) {
            website = new Facebook();
        } else if ("youtube".equals(websiteRequested)) {
            website = new YouTube();
        } else if ("stackoverflow".equals(websiteRequested)) {
            website = new StackOverflow();
        }
        website.showWebsite();
    }
}

Whoop-de-doo, more code for nothing? Actually we can go a little further and have the client rope a couple of friends into helping it find and render a requested website:

Listing of ClientWithALittleHelpFromFriends.java:

public class ClientWithALittleHelpFromFriends {
    public static void main(String... args) {
        WebsiteFinder finder = new WebsiteFinder();
        WebsiteRenderer renderer = new WebsiteRenderer();
        renderer.render(finder.findWebsite(args[0]));
    }
}

Listing of WebsiteFinder.java:

public class WebsiteFinder {
    public Website findWebsite(String websiteRequested) {
        if ("facebook".equals(websiteRequested)) {
            return new Facebook();
        } else if ("youtube".equals(websiteRequested)) {
            return new YouTube();
        } else if ("stackoverflow".equals(websiteRequested)) {
            return new StackOverflow();
        }
    }
}

Listing of WebsiteRenderer.java:

public class WebsiteRenderer {
    public void render(Website website) {
        website.showWebsite();
    }
}

Looking back at ClientWithoutInterface, it is totally coupled to both specific lookup and rendering based. It would be very difficult to manage when you get to hundreds or thousands of sites. With the Website interface in place the WebsiteFinder could easily be converted to be backed on a Map, a database or even a web based lookup to satisfy increasing scale.

Interfaces make it possible to separate a role from the component that achieves it. They make it possible to swap in alternative solutions to the same problem based on pretty much anything:

  • Current load on machine

  • Size of the data set (sorting algorithms can be picked)

  • User requesting the action being performed

查看更多
劫难
5楼-- · 2020-01-27 00:41

Well, I found lately a very useful method of using interface.

We have many objects...

public class Settings { String[] keys; int values; }
public class Car { Engine engine; int value; }
public class Surface { int power; int elseInt; }
// and maaany more (dozens...)

Now, someone is creating (i.e.) table and want to show some of objects from the list of all objects, but to show objects in the list he must write method that returns String[].

String[] toArrayString()

So he just implements this method in all classes that he need to in table

public class Settings { String[] keys; int values; public String[] toArrayString {...} }
public class Car { Engine engine; int value; } // THIS NOT
public class Surface { int power; int elseInt; public String[] toArrayString {...} }
// and maaany more (dozens...)

Now, when he creates table he is writing smth like this

public void createTable() {
    for(Object obj : allObjects) {
       if(obj instanceof Settings) {
          Settings settings = (Settings)obj;
          table.add(settings.toArrayString());
       }
       if(obj instanceof Surface) {
          // cast...
       }
       // etc multiple times...
    }
}

With interface this code can be much shorter and easier to read and maintain:

public interface ISimpleInterface { String[] toArrayString; }

public class Settings implements ISimpleInterface { String[] keys; int values; public String[] toArrayString {...} }
public class Car { Engine engine; int value; } // THIS NOT
public class Surface implements ISimpleInterface { int power; int elseInt; public String[] toArrayString {...} }

public void createTable() {
    for(Object obj : allObjects) {
       if(obj instanceof ISimpleInterface) {
          ISimpleInterface simple = (ISimpleInterface)obj;
          table.add(simple.toArrayString());
       }
    }
}

Moreover, we can implement multiple interfaces in a very clean and effective way without any derivation (derivation is sometimes impossible and not only in case, when class is using some kind of other derivation already).

查看更多
聊天终结者
6楼-- · 2020-01-27 00:44

A long time ago, I read a book (can't remember the name of it though) and it had a pretty good analogy for interfaces. If you (or your students) ever went to a Cold Stone Creamery ice cream store, this will sound kind of familiar. Cold Stone has ice cream and with the ice cream you can add several different things to the ice cream (called mix-ins at Cold Stone). These mix-ins would be analogous to interfaces. Your class (or ice cream) can have as many interfaces (or mix-ins) as you want. Adding an interface (or mix-in) will add the contents (or flavor) of that interface (or mix-in) to your class (or ice cream). Hope this helps!

查看更多
何必那么认真
7楼-- · 2020-01-27 00:45

Class, we spent the last few sessions implementing quicksort. It was difficult to sort that list of Persons by name. What would you now do, if you had to sort this list by grade? And what would you do if you had to sort a list of dinousaurs by age? The only way you know so far is to copy the code of the quicksort, and change the comparison and the types it operates on. That would work - until you find that elusive bug that always plagued your quicksort, and had to fix it in several dozen copies of that quicksort scattered all over the place.

Today, we are going to learn a better way.

We can write a quicksort without defining the order we want to sort the list into, and define that order (and only that order) separately when we invoke that quicksort.

[ insert explanation of the mechanics of interfaces and polymorphism, using the Comparator interface as example, here ]

Now, there is only a single copy of quicksort, and bugs only have to be fixed once. Also, people can use quicksort without understanding it (or if they have understood it, without thinking about its mechanics whenever you want to sort something). Also, the people writing the quicksort did not need to know the order you need your list sorted. So the interface isolated the two groups of programmers, allowing them to develop their parts of the software in isolation. This is why, in many programming languages, you will find well implemented and tested sort methods in the api, even though the programmers of these methods could not know all the types of objects and orders people want to sort into later.

查看更多
登录 后发表回答