0

I would like know opinions about the next sentence:

"In a object oriented static typed language you should declare variables and parameters as broad as possible and return types as narrow as possible"

In some extreme this afirmation could produce this code:

interface  anyInterface<T> {
    Collection<T> getCollection();
}
class anyClass implements anyInterface<Integer> {
    @Override
    public ArrayList<Integer> getCollection() {
        ...
    }
}

Thanks in advance for your opinions.

JoseLuis
  • 29
  • 2
  • The question could be refined a bit, but it's an interesting one. There's one half of the answer here: https://softwareengineering.stackexchange.com/questions/232359/understanding-programming-to-an-interface – Vincent Savard Apr 16 '18 at 13:45
  • you have reason the question is too vague. It is made so deliberately. I would like to know what is the limit of Program to interfaces, and should we return the most especific type? – JoseLuis Apr 16 '18 at 13:53
  • 2
    There is no point applying the liberal/conservative maxim to variables. Variables should have such short lives and such low visibility that changing their concrete type isn't a problem. It's really only about *parameters* and *return types*, because those have potentially infinite scope and lifetime. – Kilian Foth Apr 16 '18 at 13:58
  • @KilianFoth I believe the statement is about variables because it is all about maximizing the generality of the caller algorithm, where the caller is using variables whose instance representations can be one of many derived types. Following this advice minimizes the changes required when derived classes change. – Frank Hileman Apr 19 '18 at 00:03
  • @KilianFoth Also it ensures that the variables can be used with the maximum number of types, given that the base type(s) of type A can have larger number of derived types than the derived types of type A. – Frank Hileman Apr 19 '18 at 00:20

2 Answers2

4

"In a object oriented static typed language you should declare variables and parameters as broad as possible and return types as narrow as possible"

Taken literally, that is advising I declare all variables and parameters as Object (or whatever the base class is for a given language). That's clearly nonsense. Also, it is saying that I should not use an interface for a return type when I can use a specific type. That's also clearly nonsense.

There's some sense behind the words, eg in the .NET world, it's better to eg use IEnumerable<T> instead of T[] or List<T> if the collection is simply enumerated via eg foreach. Generally it's better to use an abstraction type (eg interface) over a concrete type. But that applies for return types from private methods as much as for parameters. So it's still not very good advice.

Finally, the "object oriented" part is irrelevant. Haskell, the functional language poster boy, has no support for OO, but it still supports polymorphism and it's strongly typed. The same "favour an abstraction type over a concrete type" applies there too.

So in conclusion, it's taken some basic sound advice, distorted it and got the message all wrong. So it's not good advice at all.

David Arno
  • 38,972
  • 9
  • 88
  • 121
  • I was just thinking "that would mean all parameters are object" – Ewan Apr 16 '18 at 13:56
  • 5
    When It's said "as broad as possible" is equal with the minimum methods in the interface as you need, obviously. Why it is applicable to the return type? what is the benefit? – JoseLuis Apr 16 '18 at 13:57
  • 1
    @JoseLuis, sorry but that is not obvious at all. "As broad as possible" *obviously* means `Object` in my view. – David Arno Apr 16 '18 at 13:58
  • 2
    Why would "as broad as possible" mean Object? This is only true if you restrict all calls to those on Object. "As broad as possible" means you use the most common base class or interface that still compiles without dynamic casting. – Frank Hileman Apr 18 '18 at 00:04
  • You should only use interfaces if the signatures are in the interface (that is making it "as broad as possible"); abstract classes are also abstract types and equivalent to interfaces in terms of OO design. – Frank Hileman Apr 18 '18 at 00:07
  • @FrankHileman, "*you use the most common base class or interface that still compiles without dynamic casting*". Really? Where does it mention that dynamic casting isn't allowed? Dynamic casting is possible; ergo if we strictly follow "as broad as possible", we have `Object` parameters and casts within the function. That's just silly. So "as broad as possible" is badly expressed. – David Arno Apr 18 '18 at 06:28
  • @DavidArno Dynamic casting is possible or not depending on the language. I do not think we should take it for granted when talking about OOP. – Stop harming Monica Apr 18 '18 at 08:22
  • @Goyo, do you have an example of a statically typed OOP language that doesn't support casting? – David Arno Apr 18 '18 at 08:24
  • @DavidArno OCaml supports OOP and does not allow downcasting, I think. – Stop harming Monica Apr 18 '18 at 08:59
  • Generally speaking, we are referring here to static typing, not dynamic typing, and proofs that ensure no dynamic type cast error can occur. – Frank Hileman Apr 18 '18 at 23:56
  • If we were talking about dynamic casting, that opens up a whole other can of worms, including the fact that methods may be dynamically resolved. The point is to catch the type errors at compile time, not run time, using static typing, so this guideline is all about static typing. – Frank Hileman Apr 19 '18 at 00:23
-1

This guideline produces code that is as reusable as possible, whilst preserving type safety as much as possible. The idea is that by using the most general type in the calling code or method parameters, that code or method can be applied to as many types as possible, in a strongly typed language.

By making the return types of your method as narrow (derived) as possible, you enable the user of that method to use all the functionality of the return type provided; a more general type (base class or interface) would restrict the subsequent usage.

This statement is not specific to object oriented programming. It is true for all strongly typed languages. It can also be extended to generic or higher kinded types, but your specific example doesn't declare any variables or parameters, so it is irrelevant.

Frank Hileman
  • 3,922
  • 16
  • 18
  • Hmm, it seems comparable to one of Uncle Bob's maxims like SRP... opaque to amateurs, obvious to veterans. A useful principle, once you understand it, but those who have enough experience to understand it probably already know and practice it anyway. – Robert Harvey Apr 18 '18 at 00:49
  • @RobertHarvey This is ancient advice...I am trying to remember where I first read it, but it is probably from Liskov's early 80's book. One thing people sometimes forget is that one original purpose of OOP was to able to write general purpose code, not in the generic sense, but in the algorithm sense, leaving the general part in the caller algorithm, and the specific part in the called object method. – Frank Hileman Apr 18 '18 at 01:09
  • 1
    I'd be very surprised if this is a quote from Liskov. Her ideas are normally incredibly sound and stand the test of time. Whereas "*static typed language[s] should declare ... return types as narrow as possible*" is fundamentally wrong advice. – David Arno Apr 18 '18 at 08:22
  • @DavidArno "Return types as narrow as possible" is correct, if you want to maximize the capabilities of the caller. Return a more general type, and you are cutting those capabilities. However, in some cases, in a public API, you may deliberately wish to hide the exact type returned, so that it can be changed in the future, or because that type is not available to the caller (internal to the called assembly/module). But this advice may be even older, as it applies to any statically typed language. It is also related to preconditions and postconditions. – Frank Hileman Apr 18 '18 at 23:50
  • @DavidArno "Fundamentally wrong": you would have to explain this one. It is related to type systems and nothing new, so I have a feeling you are misunderstanding something. – Frank Hileman Apr 18 '18 at 23:52
  • @DavidArno Read a bit about designing statically typed languages and I think you will see this advice fits right in. – Frank Hileman Apr 19 '18 at 00:25
  • @FrankHileman, "*However, in some cases, in a public API, you may deliberately wish to hide the exact type returned...*". Exactly. "*return types as narrow as possible*" is fundamentally wrong advice as it ignores abstractions. I want those return types as abstract as possible; always. Not just in public APIs. In all returns. So from an abstraction point of view, they should be as wide as possible. Then just don't use inheritance and that axis is removed from consideration and we have "make return types as wide as possible". The opposite of what's advised. Ergo it's wrong advice. – David Arno Apr 19 '18 at 05:48
  • @DavidArno The most abstract type in the language you are referring to is "object". I doubt you return object from every method, for a reason. As I said, if you ignore this advice, you limit the reusability of code. From an OOD point of view, a publicly visible type that has structure X (a set of members) is equivalent to any other type with that structure; neither is more or less abstract. By returning a more abstract type you must remove members, and limit the caller's opportunities to use your API. – Frank Hileman Apr 20 '18 at 00:22
  • @FrankHileman `Object` is a concrete type with no abstract members in any language I can think of. Therefore it clearly is the complete opposite of “the most abstract type”. And limiting the caller to only using appropriate members is a benefit of returning such an abstract type. – David Arno Apr 20 '18 at 05:23
  • @DavidArno The word "abstract" has nothing to do with abstract members here. You are using the term in a language specific way that is only relevant to the implementation of the type, not to the user of the type. In terms of abstraction, a base type is more abstract than a derived type. Object is the most abstract as it is the highest in the hierarchy. This varies of course depending on the language. There may be more than one "most abstract" type. Limiting the caller to use appropriate members is access control or visibility, not about the type hierarchy from a public perspective. – Frank Hileman Apr 20 '18 at 15:28
  • @DavidArno As I said, there are a couple exceptions to the rule, but they may be viewed as implementation details. This is very old advice and has been used for decades to maximize the reusability of the calling code (the code using the types we discuss) and the reusability of the called code (the types we discuss). Try to view this from the perspective of type theory and statically typed language design, not using unusual definitions of "abstract". Abstract data type is the full name of a data type in such a language: they are all abstract. – Frank Hileman Apr 20 '18 at 15:35