A type has many subtypes.
A subtype is also a type.
How to represent these two statements in Object oriented design?
Class AbstractType{
}
Class Type extends AbstractType{
List<Subtype> subtypes
}
Class Subtype extends Type{
}
That's basically the idea of subtyping/inheritance/polymorphism. Wiki. Exactly how to do it depends on the language/oop model.
Related
As stated here
https://standardofnorms.wordpress.com/2012/09/02/4-pillars-of-object-oriented-programming/
and as the correct answer in many job interviews - the general correct answer for the question:
"What are the 4 pillars of OOP?" is:
Abstraction
Encapsulation
Inheritance
Polymorphism
What I fail to understand is how inheritance not contained in polymorphism?
in other words, how can polymorphism be used without the use of inheritance?
The only way I know of using polymorphism is
class A{
virtual void foo(){cout<<"A";}
void bar(){cout<<"A";}
};
class B : public A{
virtual foo(){cout<<"B";}
};
A* ab = new B();
ab->foo();//prints B, using polymorphism
ab->bar();//prints A, using inheritance
A* a = new A();
a->foo();//prints A
a->bar();//prints A, obviously
As I see it, polymorphism brings with it inheritance.
Please explain why it is distinct - or why can't inheritance be discarded as a key pillar of its own. We could use polymorphism or not.
What I fail to understand is how inheritence not contained in
polymorphism?
in other words, how can polymorphism be used without the use of
inheritence?
There are 3 main types of polymorphism, and only one of them requires inheritance to work.
Ad-hoc polymorphism: This is more commonly known as function/method overloading, where multiple functions can share the same name but have different signatures. Whether or not the return type is part of the signature is language dependent.
Parametric polymorphism: in OOP, this is more commonly known as generics, where a function/method can work with multiple concrete types, and return multiple concrete types, providing compile time safety.
Subtype polymorphism: This is the one I think most people think of when they talk about polymorphism. As you know, this is when subtypes provide different implementation of their parent functions/methods.
You can read more about the different types of polymorphism from the wikipedia article here: https://en.wikipedia.org/wiki/Polymorphism_(computer_science)
As I understand the two concepts:
Inheritance
You could use inheritance without using polymorphisim. For example:
class Base {
public:
void foo();
};
class Derived : public Base {
};
int main() {
Derived d;
d.foo();
}
Here we use the common functionality of the base type in all derived types, but at no point do we do anything polymophic (we never look at the derived instance though its base interface).
Polymorphism
Polymorphism as a concept includes more than the standard inheritance based method seen most often. This is actually subtyping which is just one kind of Polymorphism.
Writing a template method is technically a form of polymophism, and function overloading another. As a concept you could argue that many other things are ways to achieve polymophism.
For example:
// This function must be given an object that has a method foo().
template <typename T> bar(T& t) {
t.foo();
}
This is polymorphic behavior without inheritance.
See: https://stackoverflow.com/a/10556406/1230538 for a really good explaination of this given by someone else.
Summary
99% you use inheritance to achieve polymorphism in most modern programming languages, but they are different concepts, and can exist/be used independently of each other.
Polymorphism without inheritance:
class A {
virtual void foo() { cout << "A"; }
};
class B {
virtual void foo() { cout << "B"; }
};
A* a = new A();
A* b = new B();
a->foo(); // prints A
b->foo(); // prints B
Both instances have the same method. So they are polymorphic. However, the method does different things because the objects are different after all.
Traditional approach to algebraic types recommends something like it:
sealed trait CompositeType
final case class LeftBranch(left : String) extends CompositeType
final case class RightBranch(right : Int) extends CompoisteType
object Trivial extends CompositeType
The problem is that I can't extend CompositeType further to have more option (just like Double extends Float offering more accuracy and providing system for backward converting from Double to Float).
Scala gives you free to define own apply and unapply methods for building and matching instances of algebraic type.
Is there any project that tries to build framework for such types?
That may be usefull for actors metaphor. Currently actors receives untyped messages (as Any implies no type restrictions) matching known types and giving safe default for others. It breaks all type strict design of scala and restricting actors with more proper types would be really nice.
update:
Example clarifing my intentions:
sealed trait CompositeType1
final case class OtherBranch(x : Int, y : Int) extends CompositeType1
object Simple extends CompositeType1
trait ComplexChoice extends CompositionType with CompositionType1
I want to create CompositionType not as a root in type hierarchy but as one solid class. That may be extended further, mixing with other classes.
Let see some normal OOP usage:
trait ContractOne
trait ContractTwo
def method(arg : ContractOne with ContractTwo)
In this example function method need an argument that fit both contracts. What does mean contract for an algebraic type? Set of available constructors and matchers. What is natural view of extending an algebraic type? Extending set of constructors with some new values (just as Double extends Float with more precise floating point numbers)
CompositeType misses this conceptions evidiently. If I mix this two algebraic types I got set intersection instead of union. This is direct effect of chosen way for representing algebraic types as set of hierarchic subtypes. It gives more freedom to span choices outside initial types, but it lacks OOP features since inheritance is taken for element construction and may not be used for extending algebraic types itself.
Haskell has only one way for adding new choices to an algebraic type:
data CompositeType = LeftBranch String | RightBranch Int | Trivial
data CompositeType1 = OtherBranch Int Int | Simple
data ComplexChoice = CompositeType | CompositeType1
ComplexChoice is defined seamlessly in concept of haskell's data types. But handling it becomes complex, since I need reroute all methods as for composition. That's why composition is a solution in scala but troublesome and boilerplate one (if there is no compiler plugin that can generate code for composition pattern)
What I really need, is something like it:
ComplexChois condense CompositeType and CompositeType1
But object hierarchies may spawn only in one direction.
So there is need in some other way for defining algebraic types.
There is space for it since infinite expanding of original trait is not something that really needed and most such traits is used with sealed keyword. Therefore some other less powerfull mechanism than extending may be used to represent data types.
As you note, objects are the end of the line for derivation of subtypes. You could, in this case, create another level of intermediate abstract type from which to derive sub-types (singleton or otherwise) of Trivial:
sealed trait Trivial extends CompositeType
object Trivia extends Trivial { ... }
class Triviality extends Trivia(...) { ... } \
...
Take for example you have a class A and class B. Class A uses a compositional type relationship that interacts with many of the class B objects. class A stored these class B objects references in an Arralist
Class A
{
ArrayList<B> beeObjects;
}
Is it bad practice to return the Array list data structure if you wanted to implement functionality that interacts with the objects in the arraylist?
Not at all. You can very well return your ArrayList beeObjects and use it. In fact, in Object Orient Programming, you would like to do as normal practice provided you have genuine usage scenario.
How to model a domain when you have a base class and 2 classes extending this base class and only one of derived class has a relationship with another object.
Example:
public abstract class Base
{
public abstract void method();
}
public class1 extends Base
{
public void method()
{
do some stuff
}
}
public class2 extends Base
{
private Class3 class3;
public void method()
{
do other stuff
}
public Class3 getClass3(){...}
public void setClass3(Class3 class3){...}
}
Is this model breaking Liskov principle? I think so because of this relation with class3, so we have to figure out how to model without this relation or to move this relation to Base. If I have a part of program that deal with Class2 to handle the relation whith Class3 I can't work with base class without cast to class2.
Is this thought right?
Clarifying...
Let's think in learning model. We have Courses and CourseClasses. We can also have a online courses and presencial courses. In presencial courses we may face with cost of this training. So costs only make sense to presencial environment. CourseClasses could have range dates or quatitative dates.
Today I have this model:
Course
{
...
}
public abstract class CourseClass
{
private Course course;
// getter and setter to course
public abstract Enrollment enroll(Person student);
}
public class QuantitativeCourseClass
{
public Enrollment enroll(Person student)
{
// enroll for quantitative
}
}
public class RangeCourseClass
{
public Enrollment enroll(Person student)
{
// enroll for range
}
}
Now I have to deal with costs and till this moment presencial course isn't important to me but now, cost only make sense to presencial enviroment.
My problem is: I need to deal with CourseClass object in cost module because I need some stuff of courseClass, but the relationship of cost is with RangeCourseClass because QuantitativeCourseClass don't make sense to prensecial environment.
The question about liskov is about how to convence my team to make some modifications in this model.
if class3 has nothing to do with base, then it should not be in the base. You can't "break" LSP, since the compiler enforces it. downcasting is not something that is preferred, but doing so doesn't break LSP.
The purpose of inheritence is to have an "is-a" relationship. A cat is-a(n) animal. A toyota is-a car.
What you're talking about is moving the toyota emblem to the car class just because you want to make things easier. That's not good design at all.
In short, it's worse design to move things to the base class than it is to downcast to the specific class.
I think you have mixed up the direction of LSP (Liskov Substitution Principle): LSP is (strong) behavioral subtyping, not strong behavioral supertyping. So LSP is not working against your example, but for your example:
Is this model breaking Liskov principle? I think so because of this
relation with class3, so we have to figure out how to model without
this relation or to move this relation to Base. If I have a part of
program that deal with Class2 to handle the relation with Class3 I
can't work with base class without cast to class2.
Your model is not breaking LSP. If you have a part of program that uses some variable var that deals specifically with Class2 (i.e. parts not present in Base), you need to declare var to be of Class2. So no downcast is necessary. And LSP guarantees that var behaves as Base, too, so no explicit upcast is necessary, either.
As i understand, you can not view the problem without knowing the problem aspects (geometry, for example). So, i can not understand meaning of your architecture. For example, the famous LSP violation Example:
Square:Rectangle -it looks fine, when it stand in "side". But, when you start use and you put some functions around, you can see the problem.
As title says, the meaning of both eludes me.
Inheritance expresses a is-a relationship, while composition expresses a has-a relationship between the two classes.
An example for composition is a polygon. It has a ordered sequence of Points. In C++ terms:
struct Polygon {
std::vector<Point> points;
};
While an logic_error is a exception:
struct logic_error : public exception {
};
Just google Inheritance vs Composition you'll get alot of results.
Using java as an example
public class Banana extends Fruit{ //<---Inheritance Banana is-a Fruit
private Peel bananaPeel; //<--Composition banana has a Peel
public Peel getPeel(){
return bananaPeel;
}
}
As pmr pointed out, inheritence is a is-a relationship, composition is a has-a relationship.
Composition is usually used for wrapping classes and to express relationships between classes that contain one another.
Inheritance is used for polymorphism, where you have a base class and you want to extend or change its functionality.
Inheritance means inheriting something from a parent.
For example, you may inherit your mother's eyes or inherit your father's build.
It means deriving properties, characteristics and behaviors from a parent class. So you can parent.walk(), parent.sleep(), parent.sleep() or whatever.
Containership or maybe composition is a bit hard to explain.
Or maybe a car has a brake. The car is composed of a brake. But the brake isn't inheriting from a brake..different concepts. I know very weird explanation..but that's how much I can do I guess.
Let's look at this code:
class Parent
{
public :
void sleep() ; void eat() ; void walk() ;
string eyeColor; int height ;
};
class Child: public Parent
{
}
So the Child class can inherit the functions and attributes of the Parent but of course it may have the eye color of the mother or father.. Even if the childs' attributes are different it can still do whatever the Parent can do.
Now composition is another thing. A Child can have a toy or a Parent can have a child. So I could do:
class Toy
{
string model ;
};
class Child
{
Toy transformersToy ;
};
So the Child has the transformers toy now.. but does Child inherit the transformersToy.model attribute? No, because it isn't inheriting.
inheritance is a relationship between classes, containership is relationship between instance of classes.
inheritance is the dynamic polymorphism, that these days change its functionality to a technic of a code reuse. but the composition is a technic for ensuring capability of implementation.
One main point I see is the ownership on the objects. Inheritance doesnt own/give any thing it just gives the characteristics of the base class. While Composition gives the owner ship to the created object.
In the first example polygon has a vector of points. So polygon owns/contains points in it.
While in inheritance you can have/use/extend the existing characteristics of the base class.