When returning an object from a method, should its type be determined as much as possible, or vice versa?

PS This question I have matured while reading the book "Perfect Code" S. McConnell. Here is a quote: "When returning an object from a method, you should usually return the most specific type of object." What made me doubt and I decided to ask the guru)

  • Is this a question at all or applied to a specific language? - gecube
  • On the contrary: D - neoascetic
  • On the contrary =))) - Sexy Girl
  • @ Ygar Tsimoshka, If you add more clarifying information, you will speed up the receipt of the answer. - Deleted
  • those. Do you doubt the competence of McConnell? - Specter

3 answers 3

"Abstraction identifies the essential characteristics of some object, distinguishing it from all other types of objects and, thus, clearly defines its conceptual boundaries from the point of view of the observer.

Abstraction focuses on the external features of the object and allows you to separate the most essential behavioral features from non-essential ones. "

from the book "Object-Oriented Analysis and Design" Grady Booch

Return an object as specific as possible for this particular task. If you do not do this, in some cases you will have to use a type conversion, which is not good from the OOP point of view.

The rest of the information about the object must be hidden. The less information is known about the object, the easier this information will be managed.

    If the language is strictly typed, then, probably, it should be as much as possible defined. If not, see the circumstances that you are returning: if you return exactly an object with data (in the JS sense, for example), then this is a ValueObject and let it be just a set of key-value pairs. And if you return an instance of a class (from a factory, for example), then, of course, it is worth it to have a type.

    By the way, here I’ve got the answer and thought it over - but how is it, “maximally determined”?

    • one
      > and how is it, "as defined as possible"? in the example of C #, the IEnumerable<T> interface is the “minimally defined” type, and the List<T> class that this and some other interfaces implements is the “maximally defined” typeSpecter
    • It became a little clearer, but vsezh ... The same C # knows how to return an instance of a class, and only if I am not mistaken - everything is an instance. Then, in any case, we will return an instance of the class that either implemented the interface or did not implement it. It turns out, if the class is not implemented - the minimum defined, if implemented - the maximum? Do not think that I find fault - I really want to understand and understand. - SoWa
    • one
      I'll show you an example: public IEnumerable <int> Method () {// return Enumerable.Range (0,10); // return new int [] {1,2,3}; // return new List <int> {1,2,3}; } as you can see if the method returns an interface, you can return any type that implements this interface, but it is undesirable to do so, if you need for example access by index, then you will have to cast. In general, it all depends on the situation - Specter
    • one
      on the other hand, the type of the argument should be the least stringent: public IEnumerable <int> Method (IEnumerable <int> value) {...} again, to make the method more flexible: Method (Enumerable.Range (0.10)); Method (new int [] {1,2,3}); Method (new List <int> {1,2,3}); - Specter
    • Understood thanks! - SoWa

    All arguments parameters and the return type should be abstracted as much as possible, only without losing the functionality.

    Sample code with comments:

     // абстракное рисуемое "тело" interface IDrawable { void paint(); } // точка class Point implements IDrawable { public int x; public int y; public void moveTo(Point p) { this.x = px; this.y = py; } public Point getClone() { Point point = new Point(); point.moveTo(this); return point; } // будем рисовать точку стандартного цвета @Override public void paint() { // предположим что 'из за нюансов реализации' // для прорисовки нам надо использовать копию обьекта Point cloned = getClone(); // рисуем точку } } // та же точка только с цветом class Pixel extends Point { public Color color; // нет смысла переопределять данный метод для аргумента типа pixel // т.к. класс поинт уже несет всю необходимую в себе информацию // для того чтоб переместить обьект public void moveTo(Pixel p) { super.moveTo(p); } @Override public void paint() { // а вот здесь у нас при использовании метода getClone() // возникает ошибка, т.к. мы используем метод который определен в классе поинт // но возвращаемый им тип для нас не подходит, мы потеряли информацию о цвете. Point cloned = getClone(); // этот метод надо переопределить в классе пиксель // рисуем цветную точку } // А вот если убрать коментарий вокруг данного метода то с методом paint // все будет ОК. getClone будет возвращать нужный нам тип // и переменной 'cloned' можно будет поменять тип на пиксель /* @Override public Pixel getClone() { Pixel cloned = new Pixel(); cloned.moveTo(super.getClone()); return cloned; } */ } // какой-то очень сложный рисуемый обьект // который состоит из других обьектов помельче class ComplexDrawing implements IDrawable { private List<IDrawable> items = new LinkedList<IDrawable>(); // можно, но не имеет смысла. ведь для прорисовки обьекта вполне достаточно // просто иметь ссылку на интерфейс IDrawable public void add(Pixel item) { items.add(item); } // можно, но не имеет смысла. ведь для прорисовки обьекта вполне достаточно // просто иметь ссылку на интерфейс IDrawable public void add(Point item) { items.add(item); } // позволяет добавлять любые обьекты типа IDrawable public void add(IDrawable item) { items.add(item); } @Override public void paint() { for (IDrawable item : items) { item.paint(); } } }