0

I have two classes, Foo and Bar, and an Algorithm class that uses both.

class Foo {
    void method(Bar bar) {
        bar.otherMethod();
        ...
    }
}

class Bar {
    void method() {
        ...
    }

    void otherMethod() {
        ...
    }
}

class Algorithm {
    void run(Foo foo, Bar bar) {
        foo.method(bar);
        bar.method();
        ...
    }
}

The algorithm part (run method) is generic, and I want to be able to reuse it in other projects, involving other pairs of classes analogue to Foo and Bar, which I know will each have the methods named method. However, I do not want to put the Bar.otherMethod at interface level (because it is not a generic functionality that will be needed for other IFoo and IBar pairs).

For this reason, I defined two interfaces:

interface IFoo {
    void method(IBar bar);
}

and

interface IBar {
    void method();
}

and changed the signature of Algorithm.run() in order to use these interfaces, into

void run(IFoo foo, IBar bar).

The problem is that now, in the Foo class, I have to make a cast in order to use specific aspects from its associated Bar class. A similar cast would probably have to be made when I would use another pair of classes (e.g. I might have Foo2 and Bar2, where in Foo2.method I would need to cast its IBar parameter to Bar2, in order to be able to use specific functionality).

class Foo implements IFoo {
    void method(IBar bar) {
        (Bar)bar.otherMethod();
        ...
    }
}

In general, this cast is an indicator of bad design. Is there indeed a problem? What would be a better approach for my intention of having a generic Algorithm.run() method?

Edit: One relevant aspect is that the Foo and Bar implementations in practice will actually come in pairs. At some point I might have other classes, Foo2 and Bar2, where Foo2.method does not need to call Bar2.otherMethod. In this case, the initial Foo will not be compatible with Bar2, but I am not interested in such use cases -- could this have been marked through a different design?

Edit2: Changed title and text to better express that I am interested to work with one pair of Foo and Bar classes at a time.

danbanica
  • 3,018
  • 9
  • 22
  • It's a bit unclear as to what you're trying to accomplish, but if you're using Java 8 you can make `Bar.otherMethod` a default method. https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html – Zircon Apr 20 '16 at 17:00
  • If Foo.method depends on Bar.otherMethod then it's not generic, unless Bar.otherMethod is generic by your definition – Daniel Apr 20 '16 at 17:09
  • Thanks for the comments. @Daniel: I added an edit at the end of the question, which might clarify the actual situation. – danbanica Apr 20 '16 at 17:29
  • In your Foo2 and Bar2 case, will Foo2.method will use IBar? If no, then this is a different interface as well – Daniel Apr 20 '16 at 18:30
  • Yes, I would like `Foo2` and `Bar2` to also implement the `IFoo` and `IBar` interfaces. This way I could reuse the `Algorithm.run` method. – danbanica Apr 20 '16 at 18:35
  • And yes, the `Foo2.method` would also receive a `Bar2` object as parameter. – danbanica Apr 20 '16 at 18:50
  • can you maybe provide a more concrete example? i suspect there's a better solution when the details are less abstract. see [the XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – djeikyb Apr 21 '16 at 07:27
  • 2
    as an aside, good java style eschews the interface naming convention you use. follow the examples from the standard library. `Collection`, `List`, `Map`, `Set`, `SortedSet` are all good interface names. `Stack`, `ArrayList`, `TreeMap`, `HashSet`, `ConcurrentSkipListSet` are all good implementation names. notice the interface names are broad, the implementation names are specific to the implementation – djeikyb Apr 21 '16 at 07:35
  • @djeikyb: as an example, imagine developing multiple board games, where each board game will have its `Board` and `Move` implementation. I would like `Board` and `Move` to be generic, such that I could implement generic game functionality only once, but I am not interested in, *e.g.* combining a board from chess with a move from tic tac toe. – danbanica Apr 21 '16 at 07:40
  • @qwertyman what algorithm is common between chess and tic-tac-toe, or some other board game? – djeikyb Apr 21 '16 at 07:44
  • For example a min-max algorithm for implementing 'play against computer'. – danbanica Apr 21 '16 at 07:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/109766/discussion-between-djeikyb-and-qwertyman). – djeikyb Apr 21 '16 at 07:46

4 Answers4

1

Do Foo and Bar need to be different types? It sounds like each Foo and Bar are tightly coupled. Maybe they ought to be combined.

djeikyb
  • 4,470
  • 3
  • 35
  • 42
  • This is a good potential solution, but it is more natural to have them as separated classes. They are not tightly coupled (they serve different purposes), but indeed, each `IFoo` implementation will have its associated `IBar` implementation which it will be compatible with. – danbanica Apr 21 '16 at 07:32
  • @qwertyman That's precisely the coupling I mean. If there is only one particular `IFoo` implementation for an `IBar` implementation, those classes are tightly coupled, despite having different responsibilities. It's hard to give a better answer without more concrete details. – djeikyb Apr 21 '16 at 07:41
1

You may want to leverage generics. Perhaps you have:

interface Bar {
  void frob();
}

interface Foo<T extends Bar> {
  void frood(T bar);
}

Then when you write:

Foo<SomeBar> foo = // ...
SomeBar bar = // ...
foo.frood(bar);

the Foo implementation knows that it has, not just any Bar, but specifically a SomeBar.

djeikyb
  • 4,470
  • 3
  • 35
  • 42
0

Yes, casting IBar to Bar is contradictory: you use interfaces but want to rely on a fact that all IBar implementations will be of type Bar or it subtypes, which you can not guarantee.

In your scenario declaring otherMethod in IBar interface is a lesser evil.

AdamSkywalker
  • 11,408
  • 3
  • 38
  • 76
  • Thank you for the answer -- I actually rely on the fact that all `IBar` implementations that will be used in tandem with `Foo` are of type `Bar` (or its subtypes). I added an edit at the end of the question which would clarify my situation. – danbanica Apr 20 '16 at 18:10
-1

In general, this cast is an indicator of bad design...

not really, interfaces are a really good way to split what a class do and how the class can do it more

  • they are ideal for defining mixins
  • they allow the construction of nonhierarchical type frameworks
  • they enable safe, powerful functionality enhancements via wrapper classes

read this for more details and or take a look to this post https://stackoverflow.com/a/384067/982161

Community
  • 1
  • 1
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
  • 1
    are you sure? consider a method that takes a Map. casting the input to a HashMap is unsafe. what if you get a TreeMap? LinkedHashMap? HashTable? ConcurrentSkipListMap? – djeikyb Apr 20 '16 at 17:53
  • On the other hand, casts are sometimes useful. In my situation, if the cast throws an exception, it indicates that the code was executed with an improper `IFoo` - `IBar` pair. – danbanica Apr 20 '16 at 21:00
  • @qwertyman if it's possible to have an improper `IFoo`-`IBar` pair, then interfaces are likely not the best way to express the constraints you need. – djeikyb Apr 21 '16 at 00:02
  • @djeikyb: probably -- this is actually the main point of the question (i.e. how should the design be done in order to suggest such constraints, and still be able to reuse the generic functionality). I understand that I've focused on some less relevant details when I wrote the question (it was interesting to realize how easy it is to transmit things which are perceived completely different than intended!) – danbanica Apr 21 '16 at 06:37