In object oriented programming we have uses, has-a, is-a relationships. I wonder what would be the relation between earth and the sun, Earth revolves around the sun?
What is the best way of learning object oriented analysis, design and programming?
Objects, classes and is-a relationships
In your object oriented domain model there are two main objects:
an object Earth of class Planet (instantiation, is-a)
an object Sun of class Star (instantiation, is-a)
Planet and Star are special kinds of CelestialObject (specialization, is-a)
Association of classes and objects
There is an association between Sun and Earth: Earth rotates around the Sun, captured by its gravity.
If we think in more general terms, any Star could have several Planet that rotate around. It may as well have no Planet at all. Conversely, most of the Planet rotate around one single Star. Let's keep it simple for now, despite recent discoveries, which have confirmed that in very rare cases a Planet could be associated with several Star.
Such association has two point of view: from the Planet we can say has-a-Star and from the Star side, we could say has-a-Planet.
How to represent associations in a world of objects ?
A simple way to represent a has-a association is to keep a reference to the associated object. This works only for singular association, e.g. the object Earth would keep a reference to Sun.
Another way to represent a has-a association is to keep a list (more generally, a container) of reference to the associated objects. Sun would then have a list of associated Planet composed of Venus, Earth, Mars, etc...
Sometimes an association could be more complex and bear some data that is unique to the link it represents. For example, Earth is associated with the Sun and it turns around it in 365,25 days. To keep things simple, you could just consider that it's an attribute of the Planet in the solar system. But if we'd take this seriously, this duration is in reality not a property of the Earth, because the Earth also turns around the center of the galaxy but with a different duration. It's not a property of the Sun either, because Mars turns also around the Sun, but in 686,96 days.
In such cases, you can consider that the association is itself a class. An instantiation if it would be an object with a orbit duration of 365,25 days, and has-a first extremity Sun and a second extremity Earth
More about these core concepts
To learn more about these core concepts, and in a language independent fashion, you could have a look at the UML class diagram. It's purpose is to represent exactly these kind concepts. You may also have a look at this blog, which gives a nice object of classes for celestial objects, although it doesn't address the associations.
How to use the model in a simulation ?
You need to choose an OO language to implement your model. Keep a container of celestial objects. Each of this object should have some coordinates, a rotation speed, and a method that updates the coordinates based on its rotation speed and the elapsed time. Your simulation would then consist of a loop (representing the elapsed time), and at each iteration, you'd go through your list of celestial objects and for each one update the coordinates, and display the result.
Related
Composition: A class can have references to objects of other classes as members. This is called composition and is sometimes referred to as a has-a relationship.
By Deitel P.J., Deitel H.M. - Java How to Program 9th Edition.
This viewpoint is discussed in this topic:
Prefer composition over inheritance?
Composition: Composite aggregation (composition) is a "strong" form of aggregation with the following characteristics:
*it is binary association,
*it is a whole/part relationship,
*a part could be included in at most one composite (whole) at a time, and
*if a composite (whole) is deleted, all of its composite parts are "normally" deleted with it.
Found on http://www.uml-diagrams.org/composition.html
(actually, Deitel presents UML examples following this idea, in the same book, but did not bother to explain the difference).
This viewpoint is discussed in this topic:
What is the difference between association, aggregation and composition?
Fine, BOTH ARE CORRECT. And this introduces the problem of homonym concepts.
For instance: don't draw a UML model with composition arrows to exemplify the first definition: In UML, any association is a composition by Deitels' the first definition.
Here are some aspects of my question that may help in the correct answer:
How I can say (and know) which composition are we talking about?
Where we draw the line between the two definitions (in contextual terms)?
Can I say that the first is object oriented programming and the second is software engineering/modeling?
Is the UML composition a model-only concept/jargon?
Is the UML composition an UML exclusive thing? or is also applied in the programming field?
How to avoid miscommunication of "what composition are we talking about" in a team?
Please, answer with references, evidences, it is not a philosophical/opinion problem, it is a "scope" problem that I´m trying to address.
And it is not "what is composition" question.
Edit: I´m thinking if the distinction is verb x adjective: "to compose" a class (first def.) and "a composite relation" (second def.).
I found it hard to explain the difference between UML association and implementation references without explaining at least a little bit what UML associations actually are, and what they can do, so here we go.
Association & Link
Lets start by looking at what a UML Association and a link (Association's instance) are.
[11.5.3.1] An Association specifies a semantic relationship that can occur between typed instances.
[11.8.1.1] A link is a tuple of values that refer to typed objects. An Association classifies a set of links, each of which is an instance of the Association. Each value in the link refers to an instance of the type of the corresponding end of the Association.
So the following is a valid implementation of a limited association.
class Brain { }
class Head { }
a = new Brain;
b = new Head;
link = (new Array).add(a).add(b);
Ownership
[9.5.3] When a Property is owned by a Classifier other than an Association via ownedAttribute, then it represents an attribute of the Classifier.
(Note: Class is a subclass of a Classifier.)
Navigability
[11.5.3.1] An end Property of an Association that is owned by an end Class or that is a navigableOwnedEnd of the Association indicates that the Association is navigable from the opposite ends; otherwise, the Association is not navigable from the opposite ends. Navigability means that instances participating in links at runtime (instances of an Association) can be accessed efficiently from instances at the other ends of the Association. The precise mechanism by which such efficient access is achieved is implementation specific. If an end is not navigable, access from the other ends may or may not be possible, and if it is, it might not be efficient.
Why are those concepts relevant? Imagine the following example.
We see that brain is an attribute of Head class (the black dot signifies ownership by the opposite Class), and that it is navigable (the arrow).
We also see that head is NOT an attribute of Brain (no black dot ⇒ not owned by the Brain class ⇒ not an attribute of Brain), however it is still navigable. This means that in UML the head Property is held by the association itself.
The implementation could, for example, look like this (the association itself is represented by a tuple of two references (see link description earlier)).
class Head {
public Brain brain;
}
class Brain {
}
h = new Head;
b = new Brain;
h.brain = b;
link = (new Array).add(h).add(b);
So as you hopefully start to see, UML association is not such a simple concept as a has-a relationship.
Composition
Lets add another piece, composition.
[11.5.3.1] A binary Association may represent a composite aggregation (i.e., a whole/part relationship). Composition is represented by the isComposite attribute
[9.9.17] The value of isComposite is true only if aggregation is composite.
With the aggregation being
none - Indicates that the Property has no aggregation semantics.
shared - Indicates that the Property has shared aggregation semantics. Precise semantics of shared aggregation varies by application area and modeler.
composite -- Indicates that the Property is aggregated compositely, i.e., the composite object has responsibility for the existence and storage of the composed objects
Again we see, that a UML association is explicitly specifying concepts that are hard to perceive from implementation (e.g. who is responsible for object management/destruction).
Model Composition vs Object Implementation Composition
So from the description above we can construct a more precise description of what an implementation composition (has-a relationship) would be.
[Deteils] Composition: A class can have references to objects of other classes as members. This is called composition and is sometimes referred to as a has-a relationship.
McConnell [Code Complete 2, 6.3] also refers to has-a relationship as a Containment.
Neither of them however talk about HOW the objects (container-contained, composer-composite) are related to one another, who is responsible for lifecycles, or whether the contained element knows about the container.
So just by saying that objects have a has-a relationship (and call it composition), you could actually mean any of these (and several more)
So if you call something composition in programming, you can mean pretty much any relationship/reference (or rather not an inheritance), so the word by itself is not very useful.
In UML on the other hand you are trying to capture all such information about how the objects are related to one another. Therefore there's a focus on giving terms a more precise meaning. So when you call something composition in UML you have in mind a very specific has-a relationship, where the container is responsible for the lifecycle of the contained items.
Implementation of UML associations
All those extra concepts information mean that there is really no precise way how to even implement associations. This makes sense as the implementation would depend on the target programming language or environment (e.g. executable models, where the UML concepts are used as the final product).
As an example I can recommend a paper describing UML association implementation in Java with enforced concepts such as multiplicity, navigability, and visibility Implementing UML Associations in Java.
More subquestions
How I can say (and know) which composition are we talking about?
By context, or you can just ask (which is always a good thing to do when unsure). Personally I've heard the use of composition as "has-a relationship" only when differentiating from inheritance; and in the rest in terms of UML. But then again I am in academia, so my view is biased.
Where we draw the line between the two definitions (in contextual terms)?
As the "programming" term composition doesn't actually mean anything (only that it is has-a), I'd recommend drawing the line yourself and pushing others to use more precise terminology.
Can I say that the first is object oriented programming and the second is software engineering/modeling?
More or less, with all the nuances mentioned in this answer.
Is the UML composition a model-only concept/jargon?
Is the UML composition an UML exclusive thing? or is also applied in the programming field?
No, you can use it in programming to mean the same thing as it means in UML, but you might need to state it more obviously. E.g. "This class is a composite for those classes, because it manages their lifecycle.".
The point is to teach people to differentiate between regular-old has-a relationships, and relationships that have more precise semantics.
How to avoid miscommunication of "what composition are we talking about" in a team?
This is a very broad question that you could apply to any term to which you want attach special meaning (what even is software engineering?), and there is no best way. Have a team-shared vocabulary (you are probably already having a lots of specific terms in your domain), and guide people to use more precise terminology.
numbered quotes refers to sections in UML 2.5 Specifications.
To cite the UML 2.5 specification on page 110:
Sometimes a Property is used to model circumstances in which one instance is used to group together a set of instances; this is called aggregation. To represent such circumstances, a Property has an aggregation property, of type AggregationKind; the instance representing the whole group is classified by the owner of the Property, and the instances representing the grouped individuals are classified by the type of the Property. AggregationKind is an enumeration with the following literal values:
none: Indicates that the Property has no aggregation semantics.
shared: Indicates that the Property has shared aggregation semantics. Precise semantics of shared aggregation varies by application area and modeler.
composite: Indicates that the Property is aggregated compositely, i.e., the composite object has responsibility for the existence and storage of the composed objects (see the definition of parts in 11.2.3).
Personally I see it the way that notion of a composite aggregation is about object lifetime, not about static relation. A composite aggregation kills aggregate members when their parent dies. None leaves this open. And shared aggregation is a bastard that OMG should not have introduced at all since it's semantics is domain dependent.
I am trying to understand the basic OOP concept called abstraction. When I say "understand", I mean not just to learn a definition, but really have a deep understanding.
On the internet, I have seen many definitions such as:
Hiding the low level implementation and providing high level specification
and
focusing on essential qualities rather than specific examples.
I understand that the iPhone button is a great example of abstraction, since I, as a user, don't have to know how the screen is displayed, all I have to know is to press the button.
What do you think of the following conclusion, when it comes to abstraction:
Abstraction takes many specific instances of objects and extracts their common information and functions by providing a single, generalised concept.
So based on this, a class is actually an abstraction of many instances, right?
I disagree with both of your examples. An iPhone button is not an abstraction of the screen, it is an interface to use the phone. A class is also not an abstraction of its instances.
An abstraction can be thought of treating a specific concept as a form of a more general concept.
To repeat an overused example: all vehicles can move. Cars rotate wheels, airplanes use jets, trains run on tracks.
Given a collection of vehicles, instead of being burdened with knowing the specifics of each vehicles' inner workings, and having to:
car.RotateWheel();
airplane.StartJet();
train.MoveOnTrack();
we could treat these objects as the more abstract vehicle, and tell them to
vehicle.Move();
In this case vehicle is an abstraction. It does not represent any specific object, but represents the common functionality of cars, airplanes and trains and allows us to interact with these specific objects without knowing anything about them except that they are a type of vehicle.
In the context of OOP, vehicle would most likely be a base class of the more specific types of vehicles.
IMHO there are actually 2 underlying concepts that needs to be understood here.
Abstraction: The idea of dealing only with "What" of something rather than "How" of something. For example: When you call an object method you only care about what the method does and not how it does what it does. There are layers of abstraction i.e the upper layer is only interested in what the below layer does and not how it does it. Another example: When you are writing assembly instruction you only care what a particular instruction does and not how the underlying circuit in the CPU execute the instruction.
Generalization: The idea of comparing a bunch of things (objects, functions, basically anything) and figure out the commonality between them and then extracting that commonality. A class with a bunch of properties is the generalization of the instances of the classes as all the instances have the same properties but different values for those properties.
The goal of object-oriented programming is to take the real-world thinking into software development as much as possible. That is, abstraction means what any dictionary may define.
For example, one of possible definitions of abstraction in Oxford Dictionary:
The quality of dealing with ideas rather than events.
WordReference.com's definition is even more eloquent:
the act of considering something as a general quality or characteristic, apart from concrete realities, specific objects, or actual instances.
In fact, WordReference.com's one is one of possible definitions of abstraction and you should be surprised because it's not a programming explanation of abstraction.
Perhaps you want a more programming alike definition of abstraction, and I'll try to provide a good summary:
Abstraction is the process of turning concrete realities into object representations which could be used as archetypes. Usually, in most OOP languages, archetypes are represented by types which in turn could be defined by classes, structures and interfaces. Types may abstract data or behaviors.
One good example of abstraction would be that a chair made of oak wood is still a chair. That's the way our mind works. You learn that certain forms are the most basic definition of many things. Your brain doesn't see all details of a given chair, but it sees that it fulfills the requirements to consider something a chair. Object-oriented programming and abstraction just mirrors this.
Currently I am trying my best programming a little video game in the style of the old Zelda games. However, I am having some trouble with the whole OOP style of thinking. To be more specific, I don't really know how to "design" the screens.
Let's say I have a class for my sprites and loaded a Wall-sprite to have a border for a certain area, should I make an extra "wall"-class, or is the wall being a "sprite" already enough? I thought it might be senseless to define an extra class since it would not have any different variables than the actual sprite class (as my wall IS only a sprite) so I didn't consider it to be a useful idea.
I am asking this because I have a little problem with the collision detection as well: What I currently do is loading a sprite for an object only once and rendering it multiple times at several locations. But the problem is that this causes the collision only to be detected at the last position the sprite was rendered at.
It gives me more problems when I render 2 cave-entrances somewhere but my game only checks for the 2nd entrance if I "entered" it.
So I thought making an extra "entrance"-class and creating 2 completely different objects of it that are treated separately might help, but should I then also create 30 objects for my wall-sprites?
Hmmm, there is really two questions, well three, but the OOP-thinking is too non-specific for a good question. So let's see if we can answer it by answering your valid ones.
Good OO Design is centered around "Patterns" (Common solutions to a variety of Problems) in the case of your sprite re-use in OO this would be known as a "Fly-weight" Pattern. Three important structural elements in good OO and understanding them is key to "getting it". They are:
Interfaces - They are free (relatively) of operational code, and provide only method and constructor signatures(generally) to allow for clean separation of coding concerns.
Classes - Only the reusable parts(ideally) of an object they are "the Mold or Pattern" that objects are instantiated (or patterned) from.
Objects - Instances (this chair or that chair as opposed, to Chair as an ideal) of the same form of Class (the ideal Chair). Objects (ideally) should keep as instance values only that which differentiates it from other instances of the same ideal.
However, because your original sprite is not an object you are having this collision problem, because it actually is the same instance rendered again and again, the graphics pipeline does not keep all of its previous locations as separate things it actual just stores pixels(usually), once they've been translated.
In this case if each instance is an object, each instance would have its location as a local instance variable(s), while its graphical representation and collision detection method would be common to all instances of the class.
Don't think of it as having 30 whole copies in memory at once, you only have thirty copies of the instance variables. This is true if you use OO or not; in a procedural solution to get proper collision detection you would have to maintain an array of all of the places you rendered that sprite and iterate through each time, further your code would be less cleanly separated and you would have to iterate through the array for every sprite interaction as well as for updating moving sprites. With OO you could handle this with one class method call that recurses it children.
A good class structure for this simple example might be:
An Abstract Sprite Class (abstract because you will never use a non-specific Sprite) containing only code common to all sprites
A Concrete Wall Sprite Class that extends Sprite, only the code for non-moving wall sprites.
A Concrete Trigger Sprite Class (whose graphic might be clear or null) for behaviors that need to be triggered in "open spaces"
A Concrete Agent Sprite Class for moving sprites (might implement a movable interface meaning all derivatives of the class have a move() method.
A Concrete Character class that extends agent for a movable sprite that is driven by user commands.
It may seem confusing at first, but it's actually cleaner, simpler, and more maintainable doing it the OO way.
:)
I am getting really confused about OOD when designing relatively large system. Always, we talk about has-a relationship between two entities. My question is about which one owns the other?
when designing a parking lot, there are many parking space. Should the car has an data field called parking space, or should the parking space hold the car? or both?
in a library reservation system, I am assuming there is a class Library, a class Book, and a class User. Shall the user call checkout(book), or the book call checkout(user), or the library call checkout(book, user)?
It's been very confusing for me. Any comment/suggestion is welcomed.
Lily
It depends on the situation, and what you mean by "own".
In your first example there is a one-one relationship between a car and a parking space. From a database perspective you will have to make a judgement about which should "own" the other (which table 'owns' the foreign key). You would base this judgement on expected usage - for example - since a parking space is likely to remain fixed, but you have cars coming and going all the time, it might make more logical sense for the carpark to "own" the car. That's where your design skills come into play.
In the second example, it seems to me that a single book can only be checked out to one user at a time, and "checking out" is an action that occurs on a book. Therefore the correct solution is Book.checkout(user). Building on that, a user is likely to checkout more than one book at a time, so I would be inclined to do have a checkout method on Library, such that Library.checkout(Books[], user) called Book.checkout(user) in turn.
For #1, the parking space should keep a record of if it is occupied and if so, what car is in it. Otherwise to see if you could park somewhere, you would have to poll every car to see if they are in that spot.
My immediate thinking for #2 is that it should be Library.checkout(Book, User) such that you make a note that a User has checked out a specific book.
This is heavily dependent on what you're trying to do however, and you should design it in such a way that is easiest for the problem at hand.
Sometimes replicating the data in two places isn't a terrible idea as long as you keep it synchronized. For instance, if you need to know where a specific car is parked, you're going to end up wanting to keep track of that data in the Car class as well, otherwise you're going to have to poll every parking spot to know if that car is parked there.
In Object Oriented design the object can be considered an entity. At this point you may use the Entity relationship modelling to better understand who has to own what.
When you design your model you shouldn’t care how you are going to implement it. I mean you shouldn’t think who is going to own what because this is another process of the design that is when you are going to convert your model to objects (that can be data table, XML, C# object ,…. ) : only at this point against the relationship the entity got you can decide who has to own what(sometime even against the requirements as I’ll show you later).
At the design time you must focus just on the requirements you have. In the case of the car and car parking you should think about :
How many park car one can occupied ?
How many cars a park can host ?
What kind of answer has my system to answer ? EX: as user I want know where a car is parked against its car plate number (in this case the previous answer would be wrong because if you let the park own the car you should iterate through the park to get what car is on it)
As you can see it really depends by you business requirements especially when you have “one-to-one” relationship(as in this case).
So I can suggest you to have a look at “Entity relationship Modelling” and stick to its concept to better design you object model.
In this case undoubtedly parking space should hold a car(it's called aggregation), because the relationship between car and parking space isn't permanent(different cars can be parked in the same parking place in the same day)
In this case, I think, the user wants to get a book, so the GUI of this system must have some button(or smht else) that user has to click to reserve a book. So, user calls a method checkout(book) of the system(object Library represents it) to check if the book is free(or available). Then the system(Library) checks whether the book wasn't reserved earlier by other user, so it calls method Book.check() for all instances of this book. In such solution every user account in the system should have a list of reserved books which method Book.check() uses.
To think out of box, I don't think the examples you provided have a natural "has a" or "owns a" relationship, and there are more relationships than "has a" or "owns a". In my opinion, I'd like to use a loosely coupled relationship for your examples, in implementation perspective, I would use a map to describe and maintain this relationship, which means, for a parking lot and a car, I would put a map in the Parking class, and map its slots to cars, if we find a slot existing in the map, we know that slot is occupied, if not, it's a free slot, for me, it doesn't make much sense either saying car owns the slot or the slot owns the car; for the library example, the same thing, the book and its borrower are in a very loose relationship, I'd put a map in the Library class, and map the book to its borrower. And who's the guy really does the checkout action? it's either the library staff or the auto machine or simply the library, so we can have a library.checkout(user, books), and inside the method, put books and user into the map.
For the bonus, what is really a "has a" relationship scenario? not a man has a car, this is not really a "has a", even we have "has a" in the sentence (don't let the human language mislead you), this just means, inside the car's class, there is a String field called ownerName or ownerId, that's it. One example for a real "has a" relationship scenario is human has a heart or a car has an engine, which means, in the implementation, there is really a Heart class field inside the Human Class.
How beautiful the object oriented design is!
So there are many ways of structuring objects (I'm talking of OOP here). For the question, I will use the classic "Car" example of OOP. Basically, How do I know when to make the car an object, or the wheel of a car an object, when both program structures would accomplish the goal?
How do I classify and categorize the parts of an object to determine whether or not they are better suited as simple attributes or variables of an object, or if they really need to be an object themselves?
Well the first thing you have to realize is the OOAD ("Object-oriented analysis and design") is a tool and not a means to an end. What you get out of that process is a model, not a true representation of what you're modelling. That model makes certain assumptions. The purpose of that model is to solve a problem you have.
So how do you know how to design objects? How do you know if you've done it right? By the end result: has it solved your problem?
So, for the Car example, in some models a car count could simply be an integer count, for example the car traffic through an intersection in a traffic model. In such a model rarely do you care about the make, model or construction of cars, just the number. You might care about the type of vehicle to the point of is it a truck or car (for example). Do you model that as a Vehicle object with a type of Car or Truck? Or just separate carCount and truckCount tallies?
The short answer is: whichever works best.
The normal test for something being an object or not is does it have behaviour? Remember that ultimately objects = data + behaviour.
So you might say that cars have the following state:
of wheels;
Height of suspension;
Left or right drive;
Colour;
Width;
Weight;
Length;
Height;
of doors;
Whether it has a sunroof;
Whether it has a stereo, CD player, MP3 player and/or satnav;
Size of the petrol tank;
Number of cylinders;
of turbo charges and/or fuel injection;
Maximum torque;
Maximum brake-horsepower;
and so on.
Chances are you'll only care about a small subset of that: pick whatever is relevant. A racing game might go into more detail about the wheels, such as how hot they are, how worn, the width and tread type and so on. In such a case, a Wheel object could be said to be a collection of all that state (but little behaviour) because a Car has a number of Wheels and the Wheels are interchangeable.
So that brings up the second point about objects: an object can exist because of a relationship such that the object represents a complete set of data. So a Wheel could have tread, width, temperature and so on. You can't divide that up and say a Car has tread but no wheel width so it makes sense for Wheel to be an object since a Wheel in it's entirety is interchangeable.
But again, does that make sense for what're doing? That's the key question.
Don't start out by classifying things - seems like people are too eager to start building inheritance hierarchies.
write down a list of specific, concrete scenarios - what your app will do, step by step. An object model is only useful if it does what you need it to do - so start working back from the scenarios to see what common objects and behaviours you can shake out of each one.
identify the "roles" in your scenarios - not necessarily actual class names - just vague "roles" that turn up when you think through concrete scenarios for how your software will work. These roles might later become classes, interfaces, abstract classes - whatever you need - at the start they're just placeholders for doing a type of work.
Work out what each role "does". The key is having a bunch of named roles - that identify things that the objects will do. Thins is about distilling out a set of things each role can do - they might do the whole thing, or put together a bunch of other objects to do the work, or they might co-ordinate the work... it depends on your scenarios.
The most important thing in OOD/OOP - is OBJECTS DO THINGS - not what's inside them - what they do.
Don't think about inheritance early on - because it will tie you up in overcomplicated hierarchies and make you think in terms of SQL-oriented programming rather than object-oriented programming. Inheritance is just one way of sharing common code. There are lots of other ways - delegation, mixins, prototype-based programming...
Here are some guidelines I came up with to help with this:
What should be on a checklist that would help someone develop good OO software?
There are some good answers here, but possibly more than you were looking for. To address your specific questions briefly:
How do I know when to make the car an object, or the wheel of a car an object, when both program structures would accomplish the goal?
When you need to distinguish one instance from another, then you need an object. The key distinction of an object is: it has identity.
Extending this answer slightly to classes, when the behaviors and/or properties of two similar objects diverge, you need a new class.
So, if you're modeling a traffic simulation that counts wheels, a Vehicle class with a NumberOfWheels property may be sufficient. If you're modeling a racing simulation with detailed road-surface and wheel-torque physics, each wheel probably needs to be an independent object.
How do I classify and categorize the parts of an object to determine whether or not they are better suited as simple attributes or variables of an object, or if they really need to be an object themselves?
The key distinctions are identity and behavior. A part with unique existence is an object. A part with autonomous behavior requires its own class.
For example, if you're creating a very simple car-crash simulation, NumberOfPassengers and DamageResistance may be sufficient properties of a generic Vehicle class. This would be enough to tell you if the car was totalled and the passengers survived. If your simulation is much more detailed, perhaps you want to know how far each passenger was thrown in a head-on collision, then you would need a Passenger class and distinct Passenger objects in each Vehicle.
I like Wirfs-Brock's Responsibility-Driven Design (RDD) and also recommend this updated (free paper) Responsibility-Driven Modeling approach by Alistair Cockburn.
In over 15 years of OO development, whenever I've felt I'm getting lost in a software architecture, going back to the RDD basics always helps me clarify what the software is supposed to be doing and how.
If you like a test-driven approach, this article shows how to relate RDD to mocking objects and tests.
Attributes or variables are often "base" types of a language. The question is what you can sensibly abstract.
For example, you can reduce a Wheel to descriptors made up of base types like integers, floating-point values and strings, which represent characteristic attributes of any wheel: numberOfTreads, diameter, width, recommendedPressure, brand. Those attributes can all be expressed with base types to make a Wheel object.
Can you group some of those attributes into a more abstract arrangement that you can reuse, independent of a Wheel? I think so. Perhaps create a Dimensions object with the attributes diameter and width. Then your Wheel has a Dimensions object instance associated with it, instead of diameter and width. But you could think about using that Dimensions object with other objects, which may not necessarily be Wheel instances.
Going up the list, you can reduce a Car to be made up of base types, but also other objects, such as Wheel objects. It is sensible to do so, because other motor and non-motor vehicles (such as a Bicycle) also contain Wheel instances.
Abstracting Wheel and Dimensions lets you re-use these object types in different contexts you may not initially consider. It makes your life a little easier because you have less code to rewrite, in theory.
If you can create a hierarchy of objects, to the point where the deepest, lowest-level object is only made up of a few base types, that is probably a good place to start.
If it's true that "both program structures would accomplish the goal" equally well, then it doesn't matter which you pick.
If, however, the program does not have a single fixed "goal" but will evolve significantly over its lifetime, then pick either one for now, and refactor as necessary as future modifications dictate. We call it "software" for a reason.
Grow your classes bottom-up.
1) Class boundaries and semantics depend on context. Until you have a context, you don't have anything. (You may not even have a car in your example). Context is given by the user story (or use case).
2) Throw all the state and behavior suggested by the given context into one class (you could name this after the user story if you would like).
3) Use systematic Refactoring to tease this class apart into separate classes. While refactoring, use existing classes as reuse opportunities.
When you're done, you'll have a set of well-defined classes that are just enough to fulfill the needs of the given user story (and the user stories that came before).