I'm having an issue in OO design where I end up with duplicate code in 2 different classes. Here's what's going on:
In this example, I want to detect collision between game objects.
I have a base CollisionObject that holds common methods (such as checkForCollisionWith) and CollisionObjectBox, CollisionObjectCircle, CollisionObjectPolygon that extend the base class.
This part of design seems ok, but here's what's troubling me: calling
aCircle checkForCollisionWith: aBox
will perform a circle vs box collision check inside Circle subclass. In reverse,
aBox checkForCollisionWith: aCircle
will perform box vs circle collision check inside Box subclass.
Issue here is that Circle vs Box collision code is duplicate, since it's in both Box and Circle classes. Is there a way to avoid this, or am I approaching this problem the wrong way? For now, I'm leaning towards having a helper class with all the duplicate code and call it from the aCircle and aBox objects to avoid duplicates. I'm curious if there's more elegant OO solution to this, though.
What you want is referred to as multi dispatch.
Multiple dispatch or multimethods is the feature of some object-oriented programming languages in which a function or method can be dynamically dispatched based on the run time (dynamic) type of more than one of its arguments.
This can be emulated in the mainline OOP languages, or you can use it directly if you use Common Lisp.
The Java example in the Wikipedia article even deals with your exact problem, collision detection.
Here's the fake in our "modern" languages:
abstract class CollisionObject {
public abstract Collision CheckForCollisionWith(CollisionObject other);
}
class Box : CollisionObject {
public override Collision CheckForCollisionWith(CollisionObject other) {
if (other is Sphere) {
return Collision.BetweenBoxSphere(this, (Sphere)other);
}
}
}
class Sphere : CollisionObject {
public override Collision CheckForCollisionWith(CollisionObject other) {
if (other is Box) {
return Collision.BetweenBoxSphere((Box)other, this);
}
}
}
class Collision {
public static Collision BetweenBoxSphere(Box b, Sphere s) { ... }
}
Here's it in Common Lisp:
(defmethod check-for-collision-with ((x box) (y sphere))
(box-sphere-collision x y))
(defmethod check-for-collision-with ((x sphere) (y box))
(box-sphere-collision y x))
(defun box-sphere-collision (box sphere)
...)
This is a typical pitfall in OO development. I once also tried to solve collisions in this manner - only to fail miserably.
This is a question of ownership. Do Box class really owns the collision logic with circle? Why not the other way round? Result is code duplicity or delegating collision code from circle to Box. Both are not clean. Double dispatch doesn't solve this - same problem with ownership...
So you are right - you need third party functions/methods which solves particular collisions and a mechanism that selects the right function for two objects that are colliding (here double dispatch can be used, but if number of collision primitives is limited then probably 2D array of functors is faster solution with less code).
I had the same problem (working in Objective C), and a workaround I found for this is to define an external function to solve the collision when I already know types for both objects.
For example, if I have Rectangle and Circle, both implementing a protocol (kind of interface for this language) Shape..
#protocol Shape
-(BOOL) intersects:(id<Shape>) anotherShape;
-(BOOL) intersectsWithCircle:(Circle*) aCircle;
-(BOOL) intersectsWithRectangle:(Rectangle*) aRectangle;
#end
define intersectsWithCircle for Rectangle, and intersectsWithRectangle for Circle like this
-(BOOL) intersectsWithCircle:(Circle*) aCircle
{
return CircleAndRectangleCollision(aCircle, self);
}
and ...
-(BOOL) intersectsWithRectangle:(Rectangle*) aRectangle
{
return CircleAndRectangleCollision(self, aRectangle);
}
Of course it doesn't attack the coupling problem of Double Dispatch, but at least it avoids code duplication
You should use checkForCollisionWith: aCollisionObject and since all your objects are extending CollisionObject you can put all common logic there.
Alternatively you could use the delegation design pattern to share common logic between different classes.
You don't say what language you are using, so I presume it is something like Java or C#.
This is a situation where multimethods would be an ideal solution, but most languages do not support them. The usual way to emulate them is with some variation of the visitor pattern - see any good book on design patterns.
Alternatively have a separate CollisionDetection class that checks for collisions between pairs of objects, and if two objects collide then it calls the appropriate methods on the objects, e.g. bomb.explode() and player.die(). The class could have a big lookup table with each object type along the rows and columns, and the entries giving the methods to call on both objects.
Perhaps you could have a collision object which contains the methods for testing for different types of collisions. The methods could return other objects which contain the collision point and other necessary information.
First option: Make the collision directional. For example, if the box is stationary, it doesn't check its own collisions with anything else; but the moving circle checks collision with the box (and other stationary objects). This is unintuitive because all our lives we're taught "equal and opposite reactions". Pitfall: moving objects would duplicate collisions with other moving objects.
Second option: Give every object a unique ID number. In the collision checking method, only check the collision if the first parameter/object has a lower ID than the second parameter.
Say the box has id=2 and circle has id=5. Then, "box collides with circle" would be executed, since box.id < circle.id; but then when the circle is checking collisions, "circle collides with box" will just return immediately without checking the collision, because the collision would have already been checked.
Related
I have been reading about inheritance vs composition. I know there is already allot about this topic on this site, but i have a question about this specific example I hope will not be seen as duplicate.
Some guidelines I have gathered so far:
Prefer composition over inheritance.
Has-a relationship will therefore almost always mean composition.
Is-a relationship CAN mean inheritance is best, but not always.
So now for the example
I have been working on a simple game engine to practice C++. For this I have made some classes:
The Image class that contains an image that can be rendered.
The Transform class is all about positioning.
The Sprite class contains an image that can be rendered, in a specific position.
There is more of course, but let’s keep it simple for the example.
So - Sprite HAS an image and HAS a position (transform). Following the guideline that would mean composition for both. BUT I think it is just so much easier to have Sprite inherit from Transform.
Because I what to do things sprite->setPosition() and sprite ->setScale().
If a Sprite inherits from Transform, I don’t have to do anything, sprite->setPosition() automatically calls Transform::setPosition() for the sprite object.
If Sprite HAS a Transform, I would have to redirect ALL of these kind of methods to the Transform! (It's how I have been doing it so far and it seems to work fine)
I don’t want to write things like this for every positioning method: (there are quite a few already)
Sprite::setPosition(Vector2 position) {
mTransform.setPosition(Vector2 position);
}
But It seems to go against convention.
What do you all think?
When you say "easier" I think you mean "faster", because let's face it, forwarding the calls from the Sprite to the Transform component it's not difficult at all, it just takes more typing (and "less typing" is good, but I would weigh other things first). The problems of choosing inheritance over composition could not be there now, but could arise later.
With public inheritance you just have to declare the inheritance, and all the public interface from Transform is available in Sprite. All. If one day you add a "Rotate" method to the Transform, but don't want Sprites to be shown upside down, then you have to hide the base class method somehow. There are good answers here from Matthieu and CodeFirst about how to do that hiding in C++. But as Matthieu points there, by hiding the method you violate the Liskov Substitution Principle. If you had used composition, you could have just ignored that "Rotate" method in the public interface for Sprite.
With private inheritance you would be a little bit better, because it represents a "is implemented in terms of" relationship between Sprite and Transform, closer to reality than the "is a" relationship represented by public inheritance. But then you would need to put "using" statements to make available the methods from Transform in Sprite (see Eugen answer also in the previous link). So you end up still having to write extra code to achieve your purpose (although less than with composition).
With composition I feel that you have easier options to deal with design problems. Do you want to simply call the component method? You forward the call. Do you want to ban the component method? You don't add it to the Sprite interface. Do you want to add functionality to one method (e.g. keeping the Transform scale from being set to such value that the size of the Sprite Image is too small)? You add the method to the interface but do some extra processing/checkings before/after calling the Transform component...
If you can get your hands on Scott Meyers' Effective C++ book, you have two items that are worth reading for this discussion: "Use private inheritance judiciously" and "Model 'has-a' or 'is-implemented-in-terms-of' through composition". A final point extracted from there in favor of composition: "Both composition and private inheritance mean is-implemented-in-terms-of, but composition is easier to understand, so you should use it whenever you can"
I'm starting to learn F# with the wonderful site https://fsharpforfunandprofit.com
While reading about pattern matching in the entry about four key concepts that differentiate F# from a standard imperative language, I found this sentence (emphasis mine):
These kinds of choice types can be simulated somewhat in C# by using
subclasses or interfaces, but there is no built in support in the C#
type system for this kind of exhaustive matching with error checking.
That seems strange to me, because I think we can obtain an exactly equivalent result with a method definition in an interface (or with an abstract method in an abstract class) in C#: it enforces all the inheriting classes to implement that method, in the same way that in the F# code it enforces the draw method to provide an implementation for all the "inheriting" types.
The difference is that in the functional case all the implementations are in the same method, while in the object oriented case, each implementation is encapsulated in its class... but conceptually you get the same enforcement in both cases, so I don't see any benefit in the functional approach.
Am I missing something? Can somebody clarify this for me?
The key insight is that there are two ways of modeling domain (broadly speaking).
Let's take classes and interfaces. Say you declare IShape and make Circle, Rectangle, and all others implement it. Great. What methods are you going to have on IShape? Let's say Draw. So far so good.
Now imagine you implement yourself a dozen shapes. And then, a few months later, you find yourself in need of another operation. Let's call it IsEmpty. What do you do? You add IsEmpty to IShape, and then you go into each of the dozen classes and add IsEmpty to them. A bit of a hassle, but ok, you can do it.
A few months down the road you want to add another operation. Then another. And another. You get tired of this pretty quickly, but that's still fine, you grit your teeth, but you do it.
But then there's the next problem: somebody else who is using your library wants to add an operation of their own. What do they do? They can't modify the IShape interface, it's in your library. They can ask you to do it and republish the library (not very efficient, is it?). Or they can implement the operation in terms of if + is - i.e. if (shape is Circle) { ... } else if (shape is Rectangle) { ... }, and so on. But then they hit the very difficulty described in the article you linked - the compiler won't protect them from missing a shape!
On the other hand, take discriminated unions. You describe the union, and then add operations all you want, left and right. Every operation handles all cases within itself (and the compiler verifies that all cases are indeed handled), and you can even add new operations in other projects that reference your library without modifying the original code. Total nirvana!
But then, a few months down the road, you find out that you need another case - say Triangle. You can, of course, add this case to the type, but then you'd have to go and add handling for it in every single operation. Even worse: those people who are using your library - their code will break when they get the latest version, and they will have to modify their additional operations as well. Tedious!
So it seems that there are two different mutually exclusive ways:
Either you make it easy to add new cases, but hard to add new operations (aka "open world" model).
Or you make it easy to add new operations, but hard to add new cases (aka "closed world" model).
This is a well known problem in language design. So well known it has its own name - the "Expression Problem". There are actually languages that let you have your cake and eat it, too - Haskell has type classes, Clojure has protocols, Rust has traits, etc. Neither of the solutions that I've seen are elegant enough in practice, to the point of getting one to wonder whether solving the expression problem is even worth it.
F# does not solve this problem[1] - you can't have both ways at the same time. However, F# at least supports both ways separately: classes+interfaces for "open world", discriminated unions for "closed world". C#, on the other hand, supports only "open world".
More importantly, it turns out that in real programs "closed world" modeling is far more useful than "open world". Programs modeled this way turn out to be far more understandable, less buggy, more concise. "Open world" models usually come in useful when you expect your program to be extended after it's written, by somebody you don't necessarily know - aka "plugins". This situation does happen, but not that often.
[1] if you don't count shenannigans with statically resolved type parameters, which don't reliably work in all circumstances anyway
So, conceptually, we are talking about two quite different approaches for modelling a domain.
Consider the functional approach that we see described in the article:
type Shape = // define a "union" of alternative structures
| Circle of radius:int
| Rectangle of height:int * width:int
| Point of x:int * y:int
| Polygon of pointList:(int * int) list
let draw shape = // define a function "draw" with a shape param
match shape with
| Circle radius ->
printfn "The circle has a radius of %d" radius
| Rectangle (height,width) ->
printfn "The rectangle is %d high by %d wide" height width
| Polygon points ->
printfn "The polygon is made of these points %A" points
| _ -> printfn "I don't recognize this shape"
The key point here is that Shape defines that there are four possible options: Circle, Rectangle, Polygon and Point.
I cannot invent a new union case somewhere else in my program, Shape is strictly defined to be one of these options and, when pattern matching, the compiler can check I haven't missed one.
If I use a C# style model:
interface IShape {}
class Circle : IShape {}
class Rectangle : IShape {}
class Point : IShape {}
class Polygon : IShape {}
The possible types are unbounded. In one or more other files, I can simply define some more if I feel like it:
class Triangle : IShape {}
class Pentagon : IShape {}
class Hexagon : IShape {}
You can never know how many IShapes there might be in existence.
This is not true of the F# Shape we defined above. It has four options and only four.
The discriminated union model is actually very very powerful because often, when we're modelling a domain in software, the possible states in that domain are actually a relatively small and succinct set of options.
Let's take another example from the F# for Fun and Profit site of a Shopping cart:
type ShoppingCart =
| EmptyCart
| ActiveCart of unpaidItems : string list
| PaidCart of paidItems : string list * payment: float
If I model my cart in this way, I'm vastly reducing the scope for possible invalid states because my cart can be in one of these three states and no others.
Interfaces and classes will let you model exactly the same states but they will not prevent you from also creating an arbitrary number of additional states that are completely meaningless and irrelevant to your domain.
I am reading Head First Object Oriented Design to get a better understanding of OOP concepts.
Polymorphism is explained as:
Airplane plane = new Airplane();
Airplane plane = new Jet();
Airplane plane = new Rocket();
You can write code that works on the superclass, like an airplane, but will work with any of the subclasses. :- * Hmmm.. ..I got this one.*.
It further explains:
-> So how does polymorphism makes code flexible?
Well, if you need new functionality, you could write a new subclass of
AirPlane. But since your code uses the superclass, your new class will work
without any changes to the rest of your code.
Now I am not getting it. I need to create a sublass of an airplane. For example: I create a class, Randomflyer. To use it I will have to create its object. So I will use:
Airplane plane = new Randomflyer();
I am not getting it. Even I would have created an object of a subclasses directly. Still I don't see a need to change my code anywhere when I will add a new subclass. How does using a superclass save me from making extra changes to the rest of my code?
Say you have the following (simplified):
Airplane plane = new MyAirplane();
Then you do all sorts of things with it:
List<Airplane> formation = ...
// superclass is important especially if working with collections
formation.add(plane);
// ...
plane.flyStraight();
plane.crashTest();
// ... insert some other thousand lines of code that use plane
Thing is. When you suddenly decide to change your plane to
Airplane plane = new PterdodactylSuperJet();
all your other code I wrote above will just work (differently, of course) because the other code relies on the interface (read:public methods) provided by the general Airplane class, and not from the actual implementation you provide at the beginning. In this way, you can pass on different implementations without altering your other code.
If you hadn't used an Airplane superclass and just written MyAirplane and PterdodactylSuperJet in the sense that you replace
MyAriplane plane = new MyAirplane();
with
PterdodactylSuperJet plane = new PterdodactylSuperJet();
then you have a point: the rest of your code may still work. But that just happens to work, because you wrote the same interface (public methods) in both classes, on purpose. Should you (or some other dev) change the interface in one class, moving back and forth between airplane classes will render your code unusable.
Edit
By on purpose I mean that you specifically implement methods with the same signatures in both MyAirplane and PterodactylSuperJet in order for your code to run correctly with both. If you or someone else change the interface of one class, your flexibility is broken.
Example. Say you don't have the Airplane superclass and another unsuspecting dev modifies the method
public void flyStraight()
in MyAirplane to
public void flyStraight (int speed)
and assume your plane variable is of type MyAirplane. Then the big code would need some modifications; assume that's needed anyway. Thing is, if you move back to a PterodactylSuperJet (e.g. to test it, compare it, a plethora of reasons), your code won't run. Whygodwhy. Because you need to provide PterodactylSuperJet with the method flyStraight(int speed) you didn't write. You can do that, you can repair, that's alright.
That's an easy scenario. But what if
This problem bites you in the ass a year after the innocent modification? You might even forget why you did that in the first place.
Not one, but a ton of modificatios had occurred that you can't keep track of? Even if you can keep track, you need to get the new class up to speed. Almost never easy and definitely never pleasant.
Instead of two plane classes you have a hundred?
Any linear (or not) combination of the above?
If you had written an Airplane superclass and made each subclass override its relevant methods, then by changing flyStraight() to flyStraight(int) in Airplane you would be compelled to adapt all subclasses accordingly, thus keeping consistency. Flexibility will therefore not be altered.
End edit
That's why a superclass stays as some kind of "daddy" in the sense that if someone modifies its interface, all subclasses will follow, hence your code will be more flexible.
A very simple use-case to demonstrate the benefit of polymorphism is batch processing of a list of objects without really bothering about its type (i.e. delegating this responsibility to each concrete type). This helps performing abstract operations consistently on a collection of objects.
Let's say you want to implement a simulated flight program, where you would want to fly each and every type of plane that's present in your list. You simply call
for (AirPlane p : airPlanes) {
p.fly();
}
Each plane knows how to fly itself and you don't need to bother about the type of the planes while making this call. This uniformity in the behaviour of the objects is what polymorphism gives you.
Other people have more fully addressed your questions about polymorphism in general, but I want to respond to one specific piece:
I am not getting it, even I would have create an object of subclasses
directly.
This is actually a big deal, and people go to a lot of effort to avoid doing this. If you crack open something like the Gang of Four, there are a bunch of patterns dedicated to avoiding just this issue.
The main approach is called the Factory pattern. That looks something like this:
AirplaneFactory factory = new AirplaneFactory();
Airplane planeOne = factory.buildAirplane();
Airplane planeTwo = factory.buildJet();
Airplane planeThree = factory.buildRocket();
This gives you more flexibility by abstracting away the instantiation of the object. You might imagine a situation like this: your company starts off primarily building Jets, so your factory has a buildDefault() method that looks like:
public Airplane buildDefault() {
return new Jet();
}
One day, your boss comes up to you and tells you that the business has changed. What people really want these days are Rockets -- Jets are a thing of the past.
Without the AirplaneFactory, you'd have to go through your code and replace possibly dozens of calls to new Jet() with new Rocket(). With the Factory pattern, you can just make a change like:
public Airplane buildDefault() {
return new Rocket();
}
and so the scope of the change is dramatically reduced. And since you've been coding to the interface Airplane rather than the concrete type Jet or Rocket, this is the only change you need to make.
Suppose you have methods in your Controller class of Planes like
parkPlane(Airplane plane)
and
servicePlane(Airplane plane)
implemented in your program. It will not BREAK your code.
I mean, it need not to change as long as it accepts arguments as AirPlane.
Because it will accept any Airplane despite of actual type, flyer, highflyr, fighter, etc.
Also, in a collection:
List<Airplane> plane; // Will take all your planes.
The following example will clear your understanding.
interface Airplane{
parkPlane();
servicePlane();
}
Now your have a fighter plane that implements it, so
public class Fighter implements Airplane {
public void parkPlane(){
// Specific implementations for fighter plane to park
}
public void servicePlane(){
// Specific implementatoins for fighter plane to service.
}
}
The same thing for HighFlyer and other clasess:
public class HighFlyer implements Airplane {
public void parkPlane(){
// Specific implementations for HighFlyer plane to park
}
public void servicePlane(){
// specific implementatoins for HighFlyer plane to service.
}
}
Now think your controller classes using AirPlane several times,
Suppose your Controller class is AirPort like below,
public Class AirPort{
AirPlane plane;
public AirPlane getAirPlane() {
return airPlane;
}
public void setAirPlane(AirPlane airPlane) {
this.airPlane = airPlane;
}
}
here magic comes as Polymorphism makes your code more flexible because,
you may make your new AirPlane type instances as many as you want and you are not changing
code of AirPort class.
you can set AirPlane instance as you like (Thats called dependency Intection too)..
JumboJetPlane // implementing AirPlane interface.
AirBus // implementing AirPlane interface.
Now think of If you create new type of plane, or you remove any type of Plane does it make difference to your AirPort?
No, Because we can say the The AirPort class refers the AirPlane polymorphically.
As far as I understand, the advantage is that, for example, in a airplane combat game, you have to update all airplanes' positions at every loop, but you have several different airplanes. Let's say you have:
MiG-21
Waco 10
Mitsubishi Zero
Eclipse 500
Mirage
You don't want to have to update their movements and positions in separate like this:
Mig21 mig = new Mig21();
mig.move();
Waco waco = new Waco();
waco.move();
Mitsubishi mit = new Mitsubishi();
mit.move();
...
You want to have a superclass that can take any of this subclasses (Airplane) and update all in a loop:
airplaneList.append(new Mig21());
airplaneList.append(new Waco());
airplaneList.append(new Mitsubishi());
...
for(Airplane airplane : airplanesList)
airplane.move()
This makes your code a lot simpler.
You are completely correct that sub-classes are only useful to those who instantiate them. This was summed up well by Rich Hickey:
...any new class is itself an island; unusable by any existing code written by anyone, anywhere. So consider throwing the baby out with the bath water.
It is still possible to use an object which has been instantiated somewhere else. As a trivial example of this, any method which accepts an argument of type "Object" will probably be given an instance of a sub-class.
There is another problem though, which is much more subtle. In general a sub-class (like Jet) will not work in place of a parent class (like Airplane). Assuming that sub-classes are interchangable with parent classes is the cause of a huge number of bugs.
This property of interchangability is known as the Liskov Substitution Principle, and was originally formulated as:
Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.
In the context of your example, T is the Airplane class, S is the Jet class, x are the Airplane instances and y are the Jet instances.
The "properties" q are the the results of the instances' methods, the contents of their properties, the results of passing them to other operators or methods, etc. We can think of "provable" as meaning "observable"; ie. it doesn't matter if two objects are implemented differently, if there is no difference in their results. Likewise it doesn't matter if two objects will behave differently after an infinite loop, since that code can never be reached.
Defining Jet as a sub-class of Airplane is a trivial matter of syntax: Jet's declaration must contain the extends Airplane tokens and there mustn't be a final token in the declaration of Airplane. It is trivial for the compiler to check that objects obey the rules of sub-classing. However, this doesn't tell us whether Jet is a sub-type of Airplane; ie. whether a Jet can be used in place of an Airplane. Java will allow it, but that doesn't mean it will work.
One way we can make Jet a sub-type of Airplane is to have Jet be an empty class; all of its behaviour comes from Airplane. However, even this trivial solution is problematic: an Airplane and a trivial Jet will behave differently when passed to the instanceof operator. Hence we need to inspect all of the code which uses Airplane to make sure that there are no instanceof calls. Of course, this goes completely against the ideas of encapsulation and modularity; there's no way we can inspect code which may not even exist yet!
Normally we want to sub-class in order to do something differently to the superclass. In this case, we have to make sure that none of these differences is observable to any code using Airplane. This is even more difficult than syntactically checking for instanceof; we need to know what all of that code does.
That's impossible due to Rice's Theorem, hence there's no way to check sub-typing automatically, and hence the amount of bugs it causes.
For these reasons, many see sub-class polymorphism as an anti-pattern. There are other forms of polymorphism which don't suffer these problems though, for example "parameteric polymorphism" (referred to as "generics" in Java).
Liskov Substitution Principle
Comparison between sub-classing and sub-typing
Parameteric polymorphism
Arguments against sub-classing
Rice's theorem
One good example of when polymorphism is useful:
Let us say you have abstract class Animal, which defines methods and such common to all animals, such as makeNoise()
You then could extend it with subclasses such as Dog, Cat, Tiger.
Each of these animals overrides the methods of the abstract class, such as makeNoise(), to make these behaviors specific to their class. This is good because obiously each animal makes a different noise.
Here is one example where polymorphism is a great thing: collections.
Lets say I have an ArrayList<Animal> animals, and it is full of several different animals.
Polymorphism makes this code possible:
for(Animal a: animals)
{
a.makeNoise();
}
Because we know that each subclass has a makeNoise() method, we can trust that this will cause each animal object to call their specific version of makeNoise()
(e.g. the dog barks, the cat meows, the cow moos, all without you ever even having to worry about which animal does what.)
Another advantage is apparent when working with a team on a project. Let's say another developer added several new animals without ever telling you, and you have a collection of animals which now has some of these new animal types (which you dont even know exist!). You can still call the makeNoise() method (or any other method in the animal superclass) and trust that each type of animal will know what to do.
The nice thing about this animal superclass is that you can a extend a superclass and make as many new animal types as you want, without changing ANYTHING in the superclass, or breaking any code.
Remember the golden rule of polymorphism. You can use a subclass anywhere a superclass type object is expected.
For example:
Animal animal = new Dog;
It takes a while to learn to think polymorphically, but once you learn your code will improve a lot.
Polymorphism stems from inheritance. The whole idea is that you have a general base class and more specific derived classes. You can then write code that works with the base class... and polymorphims makes your code not only work with the base class, but all derived classes.
If you decide to have your super class have a method, say getPlaneEngineType(), and you make a new child class "Jet which inherits from Plane". Plane jet = new Jet() will/can still access the superclass's getPlaneEngineType. While you could still write your own getJetEngineType() to basically override the superclass's method with a super call, This means you can write code that will work with ANY "plane", not just with Plane or Jet or BigFlyer.
I don't think that's a good example, since it appears to confuse ontology and polymorphism.
You have to ask yourself, what aspect of the behaviour of a 'Jet' is different from an 'Airplane' that would justify complicating the software to model it with a different sub-type? The book's preview cuts off after one page into the example, but there doesn't seem any rationale to the design. Always ask yourself if there is a difference in behaviour rather than just adding classes to categorise things - usually that's better done with a property value or composing strategies than with sub-classes.
An example (simplified from a major project I lead in the early noughties) would be that an Aeroplane is final but has various properties of abstract types, one of which is the engine. There are various ways of calculating the thrust and fuel use of an engine - for fast jets bi-cubic interpolation table of values of thrust and fuel rate against Mach and throttle (and pressure and humidity sometimes), for Rockets the table method but does not require compensation for stalling the air at the engine intake; for props a simpler parametrised 'bootstrap' equation can be used. So you would have three classes of AbstractAeroEngine - JetEngine, RocketEngine and BootstrapEngine which would have implementations of methods which returned thrust and fuel use rate given a throttle setting and the current Mach number. (you should almost never sub-type a non-abstract type)
Note that the differences between the types of AbstractAeroEngine, although related to the different real world engines, are entirely differences in the how the software calculates the engine's thrust and fuel use - you are not constructing an ontology of classes which describe a view of the real world, but specialising the operations performed in the software to suit specific use cases.
How does using a superclass save me from making extra changes to rest of my code?
As all your engine calculations are polymorphic, it means that when you create an aeroplane, you can bolt on whatever engine thrust calculation suits it. If you find you have to cater for another method of calculating the thrust (as we did, several times) then you can add another sub-type of AeroEngine - as long as the implementation it supplies provides the trust and fuel rate, then the rest of the system doesn't care about the internal differences - the AeroPlane class will still ask its engine for the thrust. The aeroplane only cares that it has an engine which it can use the same way as any other engine, only the creation code has to know the type of the engine to bolt onto it, and the implementation of ScramJetEngine only cares about supersonic jet calculations - the parts of AeroPlane which calculate lift and drag, and the strategy for flying it don't have to change.
Polymorphism is powerful given that when there's a need to change a behavior you can change it by overriding a method.
Your superclass inherits its properties and behaviors to your subclasses extended by it. Thus it is safe to implicitly cast an object whose type is also from its superclass. Those common methods to your subclasses make them useful to implement an API. With that, polymorphism gives you the ability to extend a functionality of your code.
Polymorphism gains properties and all behaviors and interfaces of the super class. So is the behavior of a plane really the same as a jet?
I want to learn how to create good object-oriented (OO) design practice for collision between two objects situation in game development.
Let's say I have a SpaceShip class and a Meteor class. When Meteor collide with the SpaceShip, the SpaceShip will be destroyed.
The question:
What class should I put the method to check if there is collision between meteor and spaceship as well as the collision resolution method (destroy the spaceship)? Is it at SpaceShip class or Meteor class? Or maybe I should put at another class, ie. GameArea or GameController class?
Note: for the sake of simplicity, assume the Meteor and SpaceShip is in form of image resource. I'm used to use Java language, but other language is okay too.
It's more natural to think that collision detection is a responsibility which does not belong to Spaceship or Meteor classes. Especially when this gets complicated with multiple collision possibilities in different directions. If you put this logic in both these classes they will need to have references to lots of other objects that are around them which wouldn't be appropriate.
You could have this on a separate class such as CollisionDetector which keeps track of coordinates of all objects in game space and detect collisions. Collision prevention also appears to be a separate responsibility that should be in a different class in my opinion. You could have a separate class CollisionResolver for this. Depending on the requirements, CollisionDetector could talk to CollisionResolver.
CollisionResolver may need to be able to talk to Spaceships, for purposes such as advising them to change direction, or to command firing missiles towards the Meteor.
CollisionDetector and CollisionResolver could sit within the GameSpace/*GameController*. etc..
This would promote Single Responsibility Principle so each component would be doing only one focussed task.
Collision detection, in my opinion, is not part of an object... it should be defined as something else - some physics manager, etc. That way your objects will be independent on the collision algorithms.
Another thing is that in games usually object consists of several layers (components): Graphics Layer, Physics Layer, Logic Layer.
That way physics manager manages only physics component of given objects.
class GameObject
{
RenderComponent m_renderComponent;
LogicComponent m_aiComponent;
PhysicsComponent m_physicsComponent;
};
Well, I usually create a (sometimes generic) GameObject class or interface that has a collides method. For example:
template< typename T = int > class GameObject
{
public:
bool collides(const GameObject& obj);
};
// usage
GameObject<int> my_obj, your_obj;
if(my_obj.collides(your_obj)) { ... };
Another thing I sometimes (but rarely) do is to create a separate GamePhysics class:
template< typename T > class GamePhysics
{
public:
/* you may make this static or the class a singleton */
void detect_collision(const T& obj, const T& obj2);
};
In Java (or any other OO language) I would place a CollisionDetected callback/event in the common ancestor class of all moving objects in your game.
This is a simplified description of a game:
In a game, there's usually a game-loop. A game-loop is like a while(true) loop that runs continuously (kind of like the main UI thread of an application) and at every step checks what has changed with the objects, what should be updated and what events should be called (and more...).
For responsiveness, this loop should cycle many times a second.
Inside this loop, a Physics Engine object should continuously update it's status. This would be an instance object of an independent class. It's this engine that should detect collisions between objects and call the CollisionDetected event on all objects that have collided.
It's an idea, not the definitive solution...
Since no one has asked yet, and I've yet to find a suitable answer; simply put: What is a Degenerate Class?
Examples of various languages would be helpful...Except UML. :P
I'm also looking for a definitive answer, here's how I've understood it so far from google:
In mathematics, degeneracy signifies the limiting case in which a class of object changes its nature so as to belong to another, usually simpler, class.
the point is a degenerate case of the circle as the radius approaches 0
the circle is a degenerate form of an ellipse as the eccentricity approaches 0
In programming, following this concept of 'collapsing' into something simpler, degeneracy seems to be used in a number of ways:
1. A class with no methods or just a main method:
Big Java:
Finally, you have seen classes with only a main method. Their sole purpose is to start a program. From a design perspective, these are somewhat degenerate examples of classes.
Effective Java 2nd edition:
Item 14: In public classes, use accessor methods, not public fields
Occasionally, you may be tempted to write degenerate classes that
serve no purpose other than to group instance fields:
// Degenerate classes like this should not be public!
class Point {
public double x;
public double y;
}
2. A class with less specificity that causes it to behave like another simpler class:
Learning Java:
For example, the class of List and List share the plain old Java class List. List is called the raw type of the generic class. Every generic has a raw type. It is the degenerate, “plain” Java form from which all of the generic type information has been removed and the type variables replaced by a general Java type like Object.
Effective Java 2nd edition:
// The worst possible legal hash function - never use!
#Override public int hashCode() { return 42; }
It’s legal because it ensures that equal objects have the same hash code. It’s atrocious because it ensures that every object has the same hash code. Therefore, every object hashes to the same bucket, and hash tables degenerate to linked lists.
3. The simplest, emptiest instance of a class possible:
Big Java:
However, sometimes you get into philosophical questions dealing with degenerate inputs: empty strings, shapes with no area, and so on.
From here I'm guessing that it's a class with no behavior (i.e. no methods).