How to design objects? - oop

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).

Related

Tips on how to balance Polymorphism/Inheritance/TypeData architecture with Prototype/Flyweight pattern in a complex card game?

I am trying to develop a card game with Kotlin, but I feel like the complex architecture decisions I feel I have to make are stalling my process.
I would like to create a solid and flexible foundation for potentially dozens of different cards with different effects, of different types (e.g. spell, vehicle, weapon...).
Intuitively I feel like giving each different Card its own subclass would be ridiculous for several reasons, such as the loss of flexibility (can't (without crazy hacks) create new kinds of cards dynamically) and escalating complexity.
On the other hand, there will be different categories of cards (types) which generally have more in common with each other than cards - so it feels like I should definitely subclass Card because otherwise I would end up with Type enums (SPELL, WEAPON, VEHICLE...) and ugly switch-statements replacing what's usually dealt with by inheritance and polymorphism.
I initially had problems conceiving how to access specific behaviors of subclasses who live in collections of superclasses, but I now understand how I can solve this using the visitor pattern - all fine so far.
My problem now is this architectural approach seems to conflict with other things I have planned and which I consider necessary or at least very worthwhile to have:
Hybrid of Prototype-Flyweight-Pattern:
I have created a 'cyclopedia' of prototype cards that can be copied. Cards consist of a shared static part and a unique dynamic part:
Naturally, I would like to create subinstances of the identifying components in the following manner:
Doing this doesn't seem to be possible in a satisfying way no matter how I approach it. There are many solutions but all seem like weak compromises to me:
Doing it the way I described it means subclasses have to save
multiple references to their components; a baseclass and one subclass
one for each. It also leads to an ugly situation where type
attributes are spread over several component-classes all dependent on
each other.
Using interfaces instead of inheritance leads to loads of code duplication.
Subclassing only the components but not card itself hides type information in the innards of cards and also demands ugly reflection
for accessing subclass traits.
Implementing a data-equivalent of type-information instead of subclassing to circumvent the limiting rules of Kotlin inheritance
hierarchies seems to be hacky and removes IDE support.
I am sure there are even more approaches I have considered over the course of the last two weeks, but these are all I can remember right now.
I know that you probably would require more complete information on what I am planning to do to give me concrete answers, but since it's too complex and I don't know all about where this is going either, I am satisfied with rough ideas in the sense of 'consider this' or 'whatever you are doing, stay away from this' or general advice about how to deal with such a 'design crysis' where things seem so complex you don't know how to start.
Of course I am ready to elaborate or detail parts of my description if it becomes necessary.
Just thinking here.
What about creating a BaseCard class with Card low-level common parts, then a class for each category that inherits from BaseCard, named like Category, where you store each category common parts, and then classes CardofSomeType inheriting from its respective Category. So in a CardofSomeType you inherit anything relevant from either class, and you just add new Card(s)ofSomeType(s) to extend your set.
About what data is instance specific or shared in the class, that's orthogonal to this and an implementation detail of each class.

Abstraction as a definition

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.

OOP: How do I deal with objects that have mutual relations?

Let's say there are two classes related to each other via some relations. For example, a Student maintains a list of the Classes he takes, and each Class has a list of Students taking it. Then I am afraid of letting the Student directly being able to modify its set of Classes, because each modification would have to be followed by a similar modification of a Class's list of Students, and vice versa.
One solution is to have a class whose sole purpose is to keep track of Class-Student relations, say Registrar. But then if some method in Student requires knowledge of its Class list, the Student needs to be passed the Registrar. This seems bad. It seems Student shouldn't have access to the Registrar, where it can also access other Students. I can think of a solution, creating a class that acts as a mediator between Student and Registrar, showing the Student only what it needs to know, but this seems possibly like overkill. Another solution is to remove from Student any method that needs to access its classes and put it instead in Registrar or some other class that has access to Registrar.
The reason I'm asking is that I'm working on a chess game in Java. I'm thinking about the Piece-Cell relations and the Piece-Player relations. If in the above example it wasn't OK for a Student to have access to the Registrar, is it OK here for a Piece to have access to the Board, since a Piece needs to look around anyway to decide if a move is valid?
What's the standard practice in such cases?
If relations can be changed - classes should be decoupled as much as possible, so along with each class create an interface, do not introduce tied relations between classes.
High level of separation you can achieve using intermediate services/helpers which encapsulates logic of communication between classes, so in this case you should not inject one class to an other even both are abstracted by interfaces, basically Student does not know anything about Class, and Class does not know anything about Student. I'm not sure whether such complexity is makes sense in your case but anyway you can achieve it.
Here is you may find a useful design pattern Mediator which can encapsulate interaction logic between two decoupled entities, take a look at it.
With the mediator pattern, communication between objects is
encapsulated with a mediator object. Objects no longer communicate
directly with each other, but instead communicate through the
mediator. This reduces the dependencies between communicating objects,
thereby lowering the coupling.
What I think you have found in your pretty nice example and explanation is that OO does not solve all problems well. As long as the responsibility is well shaped and sharp, everything is fine. And as long each responsibility fits in exactly one bucket (the class), it is pretty easy to design. But here you have a tradeoff:
If I define for each responsibility a separate class, I will get a bloated design that is pretty difficult to understand (and sometimes to maintain).
If I include for each separate responsibility at least one interface, I will get more classes and interfaces than I need.
If I decide that one of the two classes is responsible for the relation as well, this one object has more knowledge than usual about the other.
And if you introduce in each case a mediator or something similar, your design will be more complex than the problem.
So perhaps you should ask the questions:
What is the likelihood that the relation between the 2 objects will change?
What is the likelihood that the relation will exist between more 1 type of objects at each end?
Is that part of the system a highly visible part, so that a lot of other parts will interface it (and therefore will be dependent on it)?
Take the simplest solution that could possibly work and start with that. As long as the solution is kept simple, it is only your code (you don't design a library for others), there are chances that you can change the design later without hassle.
So in your concrete case,
the board field should have access to the whole board XOR
the figure on the field should have the responsibility of moving XOR
there should be an object type (ChessGame?) that is responsible for the overall knowledge about moving, blocking, attacking ...
I do think that all are valid, and it depends on your special "business case" which one is the most valid.

Object Oriented application problems in game development

I'll be as direct as I can concerning this problem, because there must be something I'm totally missing coming from a structured programming background.
Say I have a Player class. This Player class does things like changing its position in a game world. I call this method warp() which takes a Position class instance as a parameter to modify the internal position of the Player. This makes total sense to me in OO terms because I'm asking the player "to do" something.
The issue comes when I need to do other things in addition to just modifying the players position. For example, say I need to send that warp event to other players in an online game. Should that code also be within Player's warp() method? If not, then I would imagine declaring some kind of secondary method within say the Server class like warpPlayer(player, position). Doing this seems to reduce everything a player does to itself as a series of getters and setters, or am I just wrong here? Is this something that's totally normal? I've read countless times that a class that exposes everything as a series of getters/setters indicates a pretty poor abstraction (being used as a data structure instead of a class).
The same problem comes when you need to persist data, saving it to a file. Since "saving" a player to a file is at a different level of abstraction than the Player class, does it make sense to have a save() method within the player class? If not, declaring it externally like savePlayer(player) means that the savePlayer method would need a way to get every piece of data it needs out of the Player class, which ends up exposing the entire private implementation of the class.
Because OOP is the design methodology most used today (I assume?), there's got to be something I'm missing concerning these issues. I've discussed it with my peers who also do light development, and they too have also had these exact same issues with OOP. Maybe it's just that structured programming background that keeps us from understanding the full benefits of OOP as something more than providing methods to set and get private data so that it's changed and retrieved from one place.
Thanks in advance, and hopefully I don't sound too much like an idiot. For those who really need to know the languages involved with this design, it's Java on the server side and ActionScript 3 on the client side.
I advise you not to fear the fact, that player will be a class of getters and setters. What is object anyway? It's compilation of attributes and behaviours. In fact the more simple your classes are, the more benefits of an OOP you'll get in the development process.
I would breakdown your tasks/features into classes like that:
Player:
has hitpoints attribute
has position attribute
can walkTo(position), firing "walk" events
can healUp(hitpoints)
can takeDamage(hitpoints), firing "isHurt" event
can be checked for still living, like isAlive() method
Fighter extends Player (you should be able to cast Player to Fighter, when it's needed) :
has strength and other fighting params to calculate damage
can attack() firing "attack" event
World keeps track of all players:
listens to "walk" events (and prevents illegal movements)
listents to "isHurt" events (and checks if they are still alive)
Battle handles battles between two fighters:
constructor with two fighters as parameters (you only want to construct battle between players that are really fighting with each other)
listens to "attack" events from both players, calculates damage, and executes takeDamage method of the defending player
PlayerPersister extends AbstractPersister:
saves player's state in database
restores player's state from database
Of course, you game's breakdown will be much more complicated, but i hope this helps you to start thinking of problems in "more OOP" way :)
Don't worry too much about the Player class being a bunch of setters and getters. The Player class is a model class, and model classes tend to be like that. It's important that your model classes are small and clean, because they will be reused all over the program.
I think you should use the warpPlayer(player, position) approach you suggested. It keeps the Player class clean. If you don't want to pass the player into a function, maybe you could have a PlayerController class that contains a Player object and a warp(Position p) method. That way you can add event posting to the controller, and keep it out of the model.
As for saving the player, I'd do it by making Player implement some sort of serialisation interface. The player class is responsible for serializing and unserializing itself, and some other class would be responsible for writing the serialised data to/from a file.
I would probably consider having a Game object that keeps track of the player object. So you can do something like game.WarpPlayerTo(WarpLocations.Forest); If there are multiple players, maybe pass a player object or guid with it. I feel you can still keep it OO, and a game object would solve most of your issues I think.
The problems you are describing don't belong just to game design, but to software architecture in general. The common approach is to have a Dependency Injection (DI) and Inversion of Control (IoC) mechanisms. In short what you are trying to achieve is to be able to access a local Service of sorts from your objects, in order for example to propagate some event (e.g warp), log, etc.
Inversion of control means in short that instead of creating your objects directly, you tell some service to create them for you, that service in turn uses dependency injection to inform the objects about the services that they depend on.
If you are sharing data between different PCs for multiplayer, then a core function of the program is holding and synchronising that piece of state between the PCs. If you keep these values scattered about in many different classes, it will be difficult to synchronise.
In that case, I would advise that you design the data that needs to be synchronised between all the clients, and store that in a single class (e.g. GameState). This object will handle all the synchronisation between different PCs as well as allowing your local code to request changes to the data. It will then "drive" the game objects (Player, EnemyTank, etc) from its own state. [edit: the reason for this is that keeping this state as small as possible and transferring it efficiently between the clients will be a key part of your design. By keeping it all in one place it makes it much easier to do this, and encourages you to only put the absolute essentials in that class so that your comms don't become bloated with unnecessary data]
If you're not doing multiplayer, and you find that changing the player's position needs to update multiple objects (e.g. you want the camera to know that the player has moved so that it can follow him), then a good approach is to make the player responsible for its own position, but raise events/messages that other objects can subscribe/listen to in order to know when the player's position changes. So you move the player, and the camera gets a callback telling it that the player's position has been updated.
Another approach for this would be that the camera simply reads the player's position every frame in order to updaet itself - but this isn't as loosely coupled and flexible as using events.
Sometimes the trick to OOP is understanding what is an object, and what is functionality of an object. I think its often pretty easy for us to conceptually latch onto objects like Player, Monster, Item, etc as the "objects" in the system and then we need to create objects like Environment, Transporter, etc to link those objects together and it can get out-of-control depending on how the concepts work together, and what we need to accomplish.
The really good engineers I have worked with in the past have had a way of seeing systems as collections of objects. Sometimes in one system they would be business objects (like item, invoice, etc) and sometimes they would be objects that encapsulated processing logic (DyeInjectionProcessor, PersistanceManager) which cut across several operations and "objects" in the system. In both cases the metaphors worked for that particular system and made the overall process easier to implement, describe, and maintain.
The real power of OOP is in making things easier to express and manage in large complex systems. These are the OOP principles to target, and not worry as much whether it fits a rigid object hierarchy.
I havent worked in game design, so perhaps this advice will not work as well, in the systems I do work on and develop it has been a very beneficial change to think of OOP in terms of simplification and encapsulation rather than 1 real world object to 1 OOP class.
I'd like to expand on GrayWizardx's last paragraph to say that not all objects need to have the same level of complexity. It may very well fit your design to have objects that are simple collections of get/set properties. On the other hand, it is important to remember that objects can represent tasks or collections of tasks rather than real-world entities.
For example, a player object might not be responsible for moving the player, but instead representing its position and current state. A PlayerMovement object might contain logic for changing a player's position on screen or within the game world.
Before I start simply repeating what's already been said, I'll point towards the SOLID principles of OOP design (Aviad P. already mentioned two of them). They might provide some high-level guidelines for creating a good object model for a game.

OOP - How to choose a possible object candidate?

I 'm concern about what techniques should I use to choose the right object in OOP
Is there any must-read book about OOP in terms of how to choose objects?
Best,
Just write something that gets the job done, even if it's ugly, then refactor continuously:
eliminate duplicate code (don't repeat yourself)
increase cohesion
reduce coupling
But:
don't over-engineer; keep it simple
don't write stuff you ain't gonna need
It's not a precise recipe, just some general guidelines. Keep practicing.
P.S.
Code objects are not related to tangible real-life objects; they are just constructs that hold related information together.
Don't believe what the Java books/schools teach about objects; they're lying.
You probably mean "the right class", rather than "the right object". :-)
There are a few techniques, such as text analysis (a.k.a. underlining the nouns) and Class Responsibility Collaborator (CRC).
With "underlining the nouns", you basically start with a written, natural language (i.e. plain English) description of the problem you want to solve and underline the nouns. That gives you a list of candidate classes. You will need to perform several passes to refine it into a list of classes to implement.
For CRC, check out the Wikipedia.
I suggest The OPEN Toolbox of Techniques for full reference.
Hope it helps.
I am assuming that there is understanding of what is sctruct, type, class, set, state, alphabet, scalar and vector and relationship.
Object is a noun, method is a verb. Object members can represent identity, state or scalar value per field. Relationships between objects usually are represented with references, where references are members of objects. In cases, when relationships are complex, multidirectional, have arity greater than 2, represent some sort of grouping or containment, then relationships can be expressed as objects.
For other, broader technical reasons objects are most likely the only way to represent any form of information in OOP languages.
I am adding a second answer due to demian's comment:
Sometimes the class is so obvious
because it's tangible, but other times
the concept of object it's to abstract
like a db connector.
That is true. My preferred approach is to perform a behavioural analysis of the system (using use cases, for example), and then derive system operations. Once you have a stable list of system operations (such as PrintDocument, SaveDocument, SpellCheck, MergeMail, etc. for a word processor) you need to assign each of them to a class. If you have developed a list of candidate classes with some of the techniques that I mentioned earlier, you will be able to allocate some of the operations. But some will remain unallocated. These will signal the need of more abstract or unintuitive classes, which you will need to make up, using your good judgment.
The whole method is documented in a white paper at www.openmetis.com.
You should check out Domain-Driven Design, by Eric Evans. It provides very useful concepts in thinking about the objects in your model, what their function are in the domain, and how they could be organized to work together. It's not a cookbook, and probably not a beginner book - but then, I read it at different stages of my career, and every time I found something valuable in it...
(source: domaindrivendesign.org)