开发者

Whether to model a car object (and its parts such as engine) with has-a (composition) or is-a (inheritance)?

I am developing a class library which will include the object Car.

The dilemma is, Car itself will be a class with fields such as Registration Number, and other general information on the car.

But a car has an engine, chassis, etc. These objects need to be modelled too. Should they be classes embedded within Car? If not, what is the usage scenario of an embedded class?

I've learnt that composition is "part of", so you can model seperate classes and use开发者_JAVA技巧 the engine type, for example, at the field level of the car to achieve this. However, "aggregation", which is a "has a" relationship with the type being passed in the ctor, also applies (a car "has an" engine).

Which way do I go?

EDIT: I am currently on homework hence the lack of a reply from me. The class library is for a web app based around cars. I am a professional developer (I develop in .NET for a living but as a junior) so this is not a homework question.

Thanks


It really depends on your application.

For example, you could implement the wheels as separate classes, containing information about what tyre is on it, how worn it is, etc. but if your app doesn't even care about the wheels then the entire class is a waste of code.

I can see three use cases for composition:

  • The owning class has gotten overly complicated and should be broken down.
  • The owning class has multiple copies of a set of properties that could be mapped into a class. This allows you to bind all those properties together.
  • The contained object may need to be inspected or considered separately from the object that owns it (eg. you might want to move the Engine object to another car) or may be replaced as a single unit.

In summary: Use composition as a tool for encapsulating complexity or eliminating repetition. If it doesn't serve one of those purposes it probably isn't worth making a new class for.


A class should have as few responsibilities as possible and encapsulate and delegate other functionality to other classes. Lots of a small, simple classes that do one thing is a sign of a readable, stable codebase.

Yes, a car will "have" an engine, but I'd suggest using an interface for this and similar "has a" relationships. Again, depending on the professor, you might get bonus points for having a factory create different cars (appropriate, no?):

public class Car
{
    private Engine engine;
    public Car(Engine engine)
    {
        this.engine = engine;
    }

    public void accelerate()
    {
        this.engine.goFaster();
    }

    public void decelerate()
    {
        this.engine.goSlower();
    }

}

public interface Engine
{
    public void goFaster();
    public void goSlower();
}


public class ReallyFastEngine implements Engine
{
    public void goFaster()
    {
    // some code that goes really fast
    }
    public void goSlower()
    {
    // some code that goes slower
    }    
}

public class NotAsFastEngine implements Engine
{
    public void goFaster()
    {
    // some code that goes not as fast
    }
    public void goSlower()
    {
    // some code that goes slower
    }    
}

public class CarFactory()
{
    public static Car createFastCar()
    {
         return new Car(new ReallyFastEngine());
    }

    public static Car createNotAsFastCar()
    {
         return new Car(new NotAsFastEngine());
    }
}


Seeing as it is homework, and depending on the inclinations of your tutor/professor/teacher, you are probably better to go down the route of writing a separate classes for the engine, wheels and so on. Even though it may be completely over-engineered, and your application may not care about them, it is possible that your homework will be marked by standards such as:

"Did they identify an engine class"

"Does it have sensible methods like Start()"

"Mark them down for lumping everything in one big class that is actually simpler, because they clearly don't understand composition"

Or whatever, and not the kinds of standards that the more pragmatic people in this thread apply to their own designs.


Only break down the model of the car into pieces that will be exposed as separate entities outside the scope of the car. Another way to think about it is do you really understand how your car gets started when you turn the key? As far as the typical driver is concerned, everything under the hood is one big (and noisy) black box. The auto-engineers know the common parts that need maintenance by the car owner and have explicitly designed them for a different level of user interaction, things like the oil dipstick or coolant reservoir refill cap.

Can you model each piece of the car? Sure. Is it helpful to model the individual spark plugs? Probably not.

Do you need cars with different attributes like color or size? Do you need cars with different capabilities like passenger or towing capacity? The one place that is different is if you need cars with different behaviors. This is where you really need to think about modeling a Driver object which has attributes, from simple ones like reaction-time to complex ones like aggressiveness.

Modeling vehicles as examples of object orientation or inheritance is problematic because the examples don't really explain the true distinctions between essential attributes that define a class. It's not new to StackOverflow but this question isn't a duplicate either, see this SO thread. I had this same discussion with a friend of mine and posted a log of it on my blog. Read up on the different aircraft types the FAA recognizes and how the regulations for each type are subdivided. There are lots of different types of aircraft, the biggest separation is between powered and unpowered.

Check out the definitions used by the FAA:

Aircraft means a device that is used or intended to be used for flight in the air.

Airplane means an engine-driven fixed-wing aircraft heavier than air, that is supported in flight by the dynamic reaction of the air against its wings.

Airship means an engine-driven lighter-than-air aircraft that can be steered.

There is also lighter-than-air and heavier-than-air. A hot-air balloon is unpowered and lighter-than-air. A blimp is powered and lighter-than-air. A glider is unpowered and heavier-than-air. A Boeing 757 is powered and heavier-than air but adds another category of 'fixed-wing' which is unlike a helicopter which is also powered and heavier-than-air but is 'rotary-wing'.

Here is the first four in the form of a table:

                 |  Powered   |    Unpowered
---------------------------------------------------
Lighter-than-air |  Blimp     |    Hot-air balloon 
Heavier-than-air |  737       |    Glider

You get the picture.

You can't just say you'll model the engine separately from the car because a car without an engine might be a whole different animal. A car without an engine is nothing like a trailer, which also doesn't have an engine but never will either. In these cases neither 'is-a' nor 'has-a' fits in the concrete way we build objects. You don't declare a blimp as being a aircraft that 'is-a' lighter-than-air, so is a hot-air balloon. The fact that they are both lighter-than-air doesn't make them related in any way except the physics they exploit. The distinction is important because the rules and regulations that apply are different. From the other angle, we don't describe a blimp as a hot-air balloon that 'has-a' engine. The aircraft aren't physically related, the relationship is how they should be handled.

If you don't need to define your objects to that level of detail, you may not need to model them to that level of detail either.


Car will be an top hierarchy object. Including simple fields like Number, ID or description. And will have complicated fields like Engine, which is an object by itself.

So the Car will look something like:

class Car{
     String ID;
     Engine engine;
}

That a has-a relation.


One criteria you can have to decide whether the classes for Engine, Chasis etc.
needs to be present as an inner class (embedded class) is whether instance of
these classes can be used elsewhere in your application. In such cases the
decision is simple and it is to make these classes exist separately
(not as inner classes).

Even if these classes are not used elsewhere in your application then other
criteria can be testability. With these classes embedded inside and with your
design is it possible to have unit tests that can appropriately test your
code providing a good coverage.

For example say, if you have made an instance variable which references an
Engine object and this variable is being initialized in the Constructor of Car.And
your Engine class has some methods which needs to be tested. Then how can
you add unit tests to check the code in Engine class ? Probably you would
have some methods in Car class which expose the behavior or Engine class allowing
you to write unit tests. Then the question is if there is a need to expose
the behavior of Engine class wouldn't it be better that the Engine class
stands on it own?

Alternatively there might not be a need to explicitly test the methods in
Engine class and unit testing the methods in Car covers the Engine class code
as well. Then it reflects tight integration of Engine class with the Car class
and would mean it can remain as an inner class.


It depends on what it is you're trying to do. Trying to design a 'Car' class (or any other class for that matter) without an idea of the use cases is an exercise in futility.

You will design the classes and their relationships and interactions very differently depending on the use cases you're trying to enable.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜