Last week, we had the author of TDD for a Shopping Website LiveProject. Friday at 11am Ranch time, Steven Solomon will be hosting a live TDD session just for us. See for the agenda and registration link
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Paul Clapham
  • Ron McLeod
  • Jeanne Boyarsky
  • Tim Cooke
Sheriffs:
  • Liutauras Vilda
  • paul wheaton
  • Henry Wong
Saloon Keepers:
  • Tim Moores
  • Tim Holloway
  • Stephan van Hulst
  • Carey Brown
  • Frits Walraven
Bartenders:
  • Piet Souris
  • Himai Minh

What is good oo?

 
Ranch Hand
Posts: 89
Python C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I have this doubt thats been bugging me.
I'm reading Head First Java.
In it I read that to make a subclass of another class (parent class) the subclass needs to pass the IS-A test.
The concept of inheritance that's been drilled into me (thanks to my college) is that inheritance should be used only when you have common code in two or more classes.
Now I'm confused as to where to use inheritance.
Should that subclass compulsorily pass the IS-A test or can I just use inheritance to avoid typing the same code again?

Thanks in advance for any help!
 
Marshal
Posts: 75672
354
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The IS-A test should always be followed. Otherwise you end up with disasters like Stack. If you go through that link you find it extends Vector. But a stack isn't a vector. You now end up with all sorts of methods inherited from Vector which shouldn't be part of a stack.
 
Campbell Ritchie
Marshal
Posts: 75672
354
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
If you have classes like Car and Taxi can you say a Taxi IS‑A Car?
 
Ashwin Rao
Ranch Hand
Posts: 89
Python C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yes I would say taxi is a car! Am I wrong? :\
 
Bartender
Posts: 10780
71
Hibernate Eclipse IDE Ubuntu
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Ashwin Rao wrote:Should that subclass compulsorily pass the IS-A test or can I just use inheritance to avoid typing the same code again?


My opinion: The latter. [*]

My reasoning: The opportunity for reusing code far exceeds the opportunity for implementing an "IS-A" relationship.

Think about it: How often have you called Math.random(), or System.currentTimeMillis(), or String.substring()?
There's no "inheritance" involved in any of them - the first two are static methods, and the String class in Java is final - and yet they are incredibly useful, and save you having to write the code again yourself.

Inheritance - at least in the narrow "parent→child" model - allows you to cascade (and override) behaviour, and it's certainly an important thing to know about; but there are actually very few hierarchies that fit naturally into that style - particularly as Java only allows single inheritance.
Very often, you may simply want to "enhance" an existing class, or provide a "view" of it; and in those cases, some form of composition is often a better fit - especially if the "composite" implements an interface.

Animal→Dog is likely to be a hierarchy, but Car→FourWheelDrive?

I tend to fall back on my old database training when I look at this stuff; and I'd state it this way:
  • 1. Is every instance of the right-hand class an instance of the left-hand one?
  • 2. Can an instance of the right-hand class ever exist without an instance of the left-hand one?
  • If the answer to both those questions is "yes", you probably have an inheritance hierarchy; if not, you probably don't - even if it seems like it ought to be.

    In the first case above, it's pretty clear: Every Dog is an Animal and, in terms of classification, a Dog cannot exist without an Animal superclass (and probably a few others).
    In the second, it's not so obvious: Is every FourWheelDrive a Car? Doubtful, because you could also have Trucks and Gocarts and Snowmobiles, but there are other examples where the answer only becomes clear when you answer the second question (if I think of one, I'll post it).

    Indeed, I suspect that the term "4-wheel drive" is actually a misnomer, since in most cases it probably refers to a "two-axle drive", so could probably apply to a 3 or even 2-wheeled vehicle. The Moon Buggy, OTOH, was a genuine "4-wheel-drive", because it had independent motors for all four wheels.

    Although I'll probably get all sorts of posts telling me I'm wrong.

    Winston

    [*] I should perhaps add that the word "inheritance" can be used in more than one way. Classes can inherit fields from superclasses, but they can also inherit methods from interfaces (which then need to be implemented). And this is why my statement may appear to disagree with Campbell, when if fact it doesn't.
    Bottom line: True "inheritance" hierarchies of classes (ie, the "parent→child" model) are relatively unusual.
     
    Ashwin Rao
    Ranch Hand
    Posts: 89
    Python C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thanks for the help guys. I appreciate it.
     
    Ashwin Rao
    Ranch Hand
    Posts: 89
    Python C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    My reasoning: The opportunity for reusing code far exceeds the opportunity for implementing an "IS-A" relationship.



    Let's say we have two (unrelated) classes A and B that use the same code. To reuse code I create a common parent class C and use inheritance to save myself from typing. But wouldn't that create class hierarchies that are possibly incorrect?
    I'm just guessing here but I think a program written using the technique above will be harder to maintain especially if somebody else is doing the maintenance.
     
    Campbell Ritchie
    Marshal
    Posts: 75672
    354
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    There is another way to reuse code, and that follows the design principle

    Favour composition over inheritance

    Had the designers of the Stack class remembered that, they could have designed this sort of class:-There you are using only the functionality of the Vector class which you actually need. Things like size() addElementAt(int) and elementAt(int) are not relevant to stacks, so they are hidden. So you can reuse a Vector object to make something new, and you have a fully‑functional stack class in 40‑something lines.

    You can add additional methods like depth, swap, duplicate, etc. quite easily if you want an advanced stack.
     
    Campbell Ritchie
    Marshal
    Posts: 75672
    354
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Winston Gutkowski wrote: . . . but there are actually very few hierarchies that fit naturally into that style - particularly as Java only allows single inheritance. . . .

    Agree. When we started we were taught all about inheritance and how useful it is, but it is only later that you realise many things don't fit inheritance at all well and need a different approach.

    [edit: spelling correction heed→need.]
     
    Ashwin Rao
    Ranch Hand
    Posts: 89
    Python C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I think I'm finally starting to understand. Thanks for your help!
     
    Campbell Ritchie
    Marshal
    Posts: 75672
    354
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    You're welcome

    Consider your hierarchy with A B and C. If you say A extends C and B extends C you will probably have a badly‑designed hierarchy. Using composition as Winston says and in that Stack class above can allow you to reuse code for surprisingly little effort.
     
    Campbell Ritchie
    Marshal
    Posts: 75672
    354
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Winston Gutkowski wrote: . . .

  • 1. Is every instance of the right-hand class an instance of the left-hand one?
  • 2. Can an instance of the right-hand class ever exist without an instance of the left-hand one?
  • If the answer to both those questions is "yes", . . .

    Are you sure it is yes‑yes? The example with Animal→Dog looks more like yes‑no.
     
    Winston Gutkowski
    Bartender
    Posts: 10780
    71
    Hibernate Eclipse IDE Ubuntu
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Ashwin Rao wrote:Let's say we have two (unrelated) classes A and B that use the same code. To reuse code I create a common parent class C and use inheritance to save myself from typing. But wouldn't that create class hierarchies that are possibly incorrect?


    Yes.

    The business of inheritance and reuse are NOT the same: Not all reuse is inheritance-based, and not all inheritance necessarily allows reuse (although it tends to lean that way).

    Inheritance - at least in the way I think you mean it - is actually specialization: The child inherits ALL of the parent's capabilities, and adds (or redefines) some of its own.
    And, as I've tried to explain (maybe badly), those sorts of hierarchies are relatively rare; and tend to fall into the "classification" model.

    There are, however, many other forms of "inheritance" - very often interface-based - that actually do the exact opposite: they allow generalization.
    The Java Collections Framework is a classic case of this: It provides a set of basic interfaces (List, Set, Map etc), along with a bunch of skeleton and implementation classes, that allow me, as a programmer, to simply define the type of collection I want, without worrying about how it's implemented.
    If I say that a method takes a List, I don't need to know if it's an ArrayList or a LinkedList. Either will do.

    Hope that helps. If not, tell me off.

    Winston
     
    Winston Gutkowski
    Bartender
    Posts: 10780
    71
    Hibernate Eclipse IDE Ubuntu
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Campbell Ritchie wrote:Are you sure it is yes‑yes? The example with Animal→Dog looks more like yes‑no.


    OK, explain to me how you would create a Dog without first at least loading a class called Animal? (assuming the hierarchy)

    I think you're thinking of temporal (as opposed to structural) "existence" - also very important, but not really part of this thread I suspect.

    Winston
     
    Campbell Ritchie
    Marshal
    Posts: 75672
    354
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I think we are straying into that confusing minefield of double negatives.
    Can a Dog instance ever exist without an Animal instance?
    Did you mean to say something like

    Does an instance of the right side always require an instance of the left side?

     
    Winston Gutkowski
    Bartender
    Posts: 10780
    71
    Hibernate Eclipse IDE Ubuntu
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Ashwin Rao wrote:I think I'm finally starting to understand. Thanks for your help!


    Very glad, because Campbell actually pointed out an error in my previous post.

    What I should have said was "Can an instance of the right-hand class ever exist without the left-hand class" (point 2), because the distinction is structural, not object (or row) based. In a database, the key to a child table will often include the key to the parent, so it's impossible to create a child without its specific parent.
    In Java, it's more down to definition because when you create a Dog, there's only one object...that just happens to be an Animal as well.

    Hope I haven't muddied the waters too much.

    Winston
     
    Sheriff
    Posts: 16940
    286
    Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    There is a lot you can learn from hypothetical scenarios involving things like Animals and Vehicles and the like. However, one big trap that people can fall into is that of trying to bring too much of the real world into their object model. You have to remember that software objects are abstractions, which is just a way for you to simplify and organize certain concepts without getting overwhelmed and/or bogged down by complexities and details. Abstractions help us visualize, organize, and understand concepts.

    Here's what Object Orientation is really about: it's about behaviors and proper assignment of responsibilities. Software exhibits behaviors. Software does things. Software responds to stimuli. Good software has those behaviors assigned appropriately to different components, each with their clearly defined and specific responsibility to do their part in completing a task. Well designed software components respond only to stimuli that are relevant to the behaviors that they are responsible for and they respond in consistent and predictable ways.

    Bad software does things it shouldn't do and responds to stimuli that it shouldn't respond to. We often say bad software is a mess but it's a mess not because it doesn't conform to real world object relationships and behaviors. Bad software is messy because the abstractions are messy. Behaviors and responsibilities are not well partitioned and assigned. Trying to read bad code is difficult because it's hard to follow and reconstruct the mental model that the author had when he or she wrote the code. Or maybe he wrote the code in a way that truly reflected the state of his mental model: disorganized and chaotic.

    That's why the Object metaphor or paradigm fits in with a lot of people's mental models of the software because they can use their experience with things in the real world and use that knowledge and experience to form similar structures and relationships between software components. What about data? Well, that's where the ideas of encapsulation and data hiding come in, right?

    Coming back to the pitfall: You can only take analogies so far. At some point, analogies fall apart and cease to apply because that's just the nature of analogies: "This is LIKE that thing" is NOT the same as "This thing IS that thing; they are exactly the same."

    So be careful and be cognizant of where real-world concepts/relationships become less useful to apply to your software world abstractions.

    Just as an example, where in the real world do you ask a Vehicle to list it's parts? Maybe you'll see that in a Disney movie, where they are anthropomorphic but certainly not at your local car plant that made the SUV you have parked in your garage. And just where can you find a RowMapper in the real world that you can hold in your hands and manipulate? If you really think about it, it comes back to behaviors and responsibilities, right? A RowMapper has a set of related responsibilities and it has methods that allow it to respond to certain "stimuli" -- and that fits really well with a certain mental model we have when we are using the Object Oriented paradigm.
     
    Author
    Posts: 63
    6
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Nice discussion. Here are my two cents:

    When we think about reusing existing code, there are basically two ways: inheritance and composition. Inheritance is the natural choice when the participating abstractions share a "is-a" relationship. In all other cases, composition should be used to avoid problems such as stack and vector problem (as indicated by Campbell; we have also included it in our book under "Broken Hierarchy" smell).

    It is relevant here to talk about what makes a "good" inheritance hierarchy. Following enabling techniques must be employed for a smell-free hierarchy:
    Apply meaningful classification: Classify types to create a hierarchical organization using the following two steps:
    - Identify commonalities and variations among types. The commonalities are captured in a new abstraction which is referred as a supertype (this process is known as generalization). The variations are encapsulated in subtypes that inherit common behavior from supertypes.
    - Classify the supertypes and subtypes in levels to create a hierarchy. The resulting hierarchy should have general types towards the root and the specialized types towards the leaves.

    Apply meaningful generalization: As described in the previous enabling technique, generalization is the process wherein common behavior and elements between the subtypes are identified and factored out in supertypes.

    Ensure substitutability: Ensure that the types in the hierarchy follow the Liskov’s Substitution Principle (LSP); in other words, a reference of a supertype can be substituted with objects of its subtypes without altering the behavior of the program. Adherence to this principle allows object-oriented programs to leverage dynamic polymorphism.

    Avoid redundant paths: An inheritance hierarchy allows us to explicitly express the logical relationship between related types. When there are redundant paths in an inheritance hierarchy, it unnecessarily complicates the hierarchy.

    Ensure proper ordering: The key benefit of a hierarchical organization stems from its ability to provide us with a means to address the complex relationships among types. Hence, it is critical to express these relationships in an orderly fashion and maintain them consistently within the hierarchy.

    I hope it is useful.

    Reference: The above enabling techniques and corresponding text is taken from "Refactoring for Software Design Smells: Managing Technical Debt" book.
     
    Winston Gutkowski
    Bartender
    Posts: 10780
    71
    Hibernate Eclipse IDE Ubuntu
    • Likes 1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    T. Sharma wrote:Nice discussion. Here are my two cents...


    Four I'd say, and well worth a 4-cent cow.

    Winston
     
    Ashwin Rao
    Ranch Hand
    Posts: 89
    Python C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thank you everyone for taking the time to help me understand this!
     
    Winston Gutkowski
    Bartender
    Posts: 10780
    71
    Hibernate Eclipse IDE Ubuntu
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Ashwin Rao wrote:Thank you everyone for taking the time to help me understand this!


    You're most welcome. Hope it's helped.

    Winston
     
    my overalls have superpowers - they repel people who think fashion is important. Tiny ad:
    free, earth-friendly heat - a kickstarter for putting coin in your pocket while saving the earth
    https://coderanch.com/t/751654/free-earth-friendly-heat-kickstarter
    reply
      Bookmark Topic Watch Topic
    • New Topic