I'm designing a game, but I can't quite get my head around the inheritance structure. I'm normally fairly good at it, but this one just has too much overlap and I can't decide on it all.
I'm seeking to model sailing vessels - think the Age of Sail. Presumably therefore everything extends a Vessel class.
There are then several types of vessel style: rowed (galleys, canoes), square-rig, fore-and-aft rig, with different behaviour. Each of these is further subdivided into several other types. I can't decide whether this should be a series of interfaces or extensions of Vessel. Note also that there can be some cross over (a vessel can be both rowed and square rigged) which leads me to think interfaces?
Ships also have different behaviours: merchant vessels, men of war, privateer, pirates. I really can't work out whether this should be an interface or an extension of another class. There is no crossover of type in this case, however.
Finally there are several behaviours which individual ships can have. Merchants may be in a convoy (defend themselves) or independent (run away). Men of war almost always attack unless heavily outgunned... but may work in fleets, squadrons or independently. Privateers and pirates only attack if weaker - usually independently but occasionally in pairs. I'm assuming that this should be an interface too?
My big problem is that each style of ship (frigate, battleship etc) can fulfil almost any of these roles, so I can't build a simple solid inheritance structure. Frigate can't extend man-o-war because some are privateers. Sloop can't extend square rigged because some are fore and aft rigged. etcetc.
Any thoughts would be appreciated, I'm at a bit of a loose end.
Thanks
Make the "behavior" part as interfaces. That will help you assigning different behaviors to different ships without problem. Strategy Pattern is helpful here. In a nutshell, it states that the changeable and the constant properties should be separated.
For different means of movements, composition sounds like the most suitable answer at this moment.
Regarding the "but may work in fleets, squadrons or independently. Privateers and pirates only attack if weaker - usually independently but occasionally in pairs." part, I guess this has nothing to do with the inheritance tree. You can make "groups" of classes depending up on your need.
This might help you:
"There are then several types of vessel style:..." is specifying different possible behaviors. So "Movable" interface and its subclasses are for that. In the "Vessel" class, you can have a member of type "Movable". Since "Movable" is an interface, any class which implements it, is assignable to this member. So any subclass of Vessel can have any possible behavior, which we cna change at runtime as well. You can also make it an ArrayList. (Not sure if you actually want to do it or not). But if you need multiple different behaviors for a same vessel, you can do it.
When you say "Ships also have different behaviours:..." it feels like separate classes extending Vessel will satisfy this requirement. The sentence "There is no crossover of type in this case, however." makes life easier.
For the nest para "Finally there are several behaviours which individual ships can have...", you should add one more member for different possible behaviors. It will mostly be an ArrayList as one vessel will have multiple attack modes.
Fro the last para, if you can give some more details, I may be able to give some more ideas.
Ok, here are some ideas:
Vessels have one or more means of propulsion (oars, sails, etc.), which you can model by composition (e.g. have a list of propulsion methods).
Vessels use one of a variety of strategies (use the strategy pattern -- see http://en.wikipedia.org/wiki/Strategy_pattern -- for this)
Strategies which depend on the presence of other nearby ships will need some way of querying for those other ships -- so you'll need some sort of data structure that allows you to find which objects are near which other objects (look into the sort of data structures that are used for broad-phase collision detection)
As an alternative to the strategy pattern, you could switch to using a component-based design. In this setup, a vessel would be composed of one or more propulsion components, a strategy component, etc. You could then combine individual components as you see fit to make different vessels.
As an extra bonus, component-based designs are very helpful if you want your game to be data-driven, because you can just write a saver/loader for each different type of component, rather than for each possible type of vessel.
You might want to see here if you're interested in this sort of approach:
http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/
I want to provide a bit of advice based on the second paragraph of Bhusan's answer, which I quote here in full:
"Regarding the "but may work in fleets, squadrons or independently. Privateers and pirates only attack if weaker - usually independently but occasionally in pairs." part, I guess this has nothing to do with the inheritance tree. You can make "groups" of classes depending up on your need."
This leads me to believe that you additionally may want to consider the Composite pattern for certain groups of ships, at least those that are comprised of ships that all share the same behavior. See http://en.wikipedia.org/wiki/Composite_pattern where it is written that "the composite pattern describes that a group of objects are to be treated in the same way as a single instance of an object."
For instance you say "Merchants may be in a convoy (defend themselves)", but presumably they can defend themselves individually as well? This is all easier said than done of course, and my advice to you is to not over-think it and start with a very small subset of what you want to do as a protoype
You should separate the types and use the strategy pattern.
Immutable properties should be bound to the inheritance tree (e.g. a frigate won't turn into canoe, these are exact, non-behavioral types, inheriting from vessel) and everything that may change should be stored as references to behavioral types which are intrachangable. (Man-o-war is a behavioral type)
AI should be handled separately, for instance with states, but that is also have to be in a different module in your architecture.
Instead of thinking about it as strictly inheritance, I think you need to think about object Composition and how that can help make things easier.
For example: a Ship has a Behaviour. (Composition... the ship delegates to the behaviour to determine how to react to X situation)
A Pirate is a Behaviour (Inheritance from a Behaviour interface)...
You should not extend the Vessel class. Rather, a Vessel object should contain other objects that describe it. (This is known as Dependency Injection, to be annoyingly pedantic--forget I said that.) You have a Propulsion class, with instances for square-sails, fore-and-aft, and oars. You might want special instances of each for big and small hulls. You have a Behavior class to handle their attitude. Sometimes a simple integer works as well as a class. Your Armament class could be, instead, just a number of guns. (Or two numbers, one for poundage.) If you expect to ram, or have a ship loaded with rockets, or need to distinguish between long guns and carronades, you might need to go back to using the a class.
This lets you switch characteristics on the fly if you want--though you probably don't. Still, you can go from sail to using sweeps, or switch bold ship-of-the-line behavior with save-the-cargo behavior of a merchant to implement a captain losing his nerve. Anyway, it's there if you need it.
Related
I am doing an android application that has two types of users, doctors and patients.
For the most part they do the same thing but in certain activities they do different things.
For example, in the calendar activity, the doctor fills out available appointment times they have, whereas the patient fills out their medications in their calendar.
In my use case diagram I have put the steps each user type takes in separate sections. Is this the correct way to do it or should I just do one section that has one actor called 'user' that outlines the generic tasks they share.
Click here to see my use case diagram
Any feedback is greatly appreciated
Your diagram seems more like a workflow, not a Use Case. Use Cases are at a very high level of abstraction. Also, they are purely Functional decompositions of the application, where you should avoid making premature design decisions. A Use Case shows the different actors that are involved and the actions they want to perform. Making them takes a systematic approach to ensure no bits of functionality are missed.
Important elements in a Use Case Diagram are:
The various actors using the application, including those responsible for the 'backend' and maintenance.
What the actors actually want to accomplish as the main Use Cases.
All derived Use Cases that are necessary to implement the main Uses Cases. I prefer the <<includes>> relationship to show this. There may be several levels of derived Use Cases.
The re-use of Use Cases between various other Use Cases.
You should avoid:
OO-type inheritance and other OO relationships. These will cause you to make premature design decisions.
Workflow. Use Cases are independent of how the actor triggers them. Workflow is actually a design decision, to be made long after Use Case analysis.
Blocks that don't actually do anything, like your 'Home' blocks. Use Cases are functional blocks, you must be able to define what information goes in, what information goes out, and what transformations it performs.
To fix your diagram, you should focus on the core functions the users want to perform. For the patient, that would be e.g. "Book Appointment", "Edit Medicine Schedule" and "Communicate with Doctor". Then you should break these up into smaller use cases using <<include>>. For example, the "Book Appointment" use case should include showing the calendar and choosing a day.
Have fun!
Situation: Suppose we're designing the UI of Windows 9 using Java API. We need to build up 3 classes main, BuildInWindow and ApplicationWindow.
main - the window for rendering the system UI (i.e. the start botton & wallpaper page)
BuildInWindow- windows for rendering buildt-in apps (e.g. IE)
ApplicationWindow- windows for rendering apps from third party (e.g. eclipse)
all of them have to implement 3 Java API interfaces, WindowFocusListener, WindowListener and WindowStateListener and have the methods onExit() and onCrushing().
onExit() performs when the system/built-in app/ third-party app is shut down normally
onCrushing() captures any system/application crush and send system state back to server
This is the original design:
http://i.stack.imgur.com/JAJiY.png
I have some ideas of how to design it in a OO manner, but I am not sure if that's the right way. Here's my thoughts:
Create an abstract class with method onExit() and onCrushing(). Since the code of onExit()would vary from 3 classes, it should be an abstract method & onCrushing()would be same fo all classes, so it would be an concrete method
tHE MAIN WINdow should use singleton design to ensure user only create one instance of main.
Use the facade design to save the trouble of implementing 3 interfaces to three classes
My question is I don't really understand facade design, so I am not sure if it can be applied in this case. Also I am not really sure if onExit() would be different for 3 classes and onCrushing() would perform the same function.
I tried my best to explain the question clearly...if you don't understand free free to comment. Thank you very much!
I've left some questions in a comment linked to your question but here's some guidance for you:
You shouldn't create an abstract class on the basis of both BuildInwindow and ApplicationWindow both having to have methods #onExit and #onCrushing if they are not to share any implementation. Abstract classes are most useful where there is a common implementation. An interface containing these methods would be sufficient. That said, your two windows may share other functionality and, if so, it could be shared through a common superclass (abstract if it relies on subclass implementation detail). You may find the Template Method pattern useful for managing the overall window mechanism with specific tailoring for different window types. You may also find the Factory Method means of instance creation (for your window classes) will help separate the object creation and set-up from the creation mechanism.
A single shared instance would seem sensible and a singleton would serve this purpose (so long as you're able to handle termination, etc). Alternatively, your application may just launch a single Main instance - you may even just hide the constructor through package access to ensure no others are created.
The facade pattern just serves to simplify a complex interface. It mainly does this by rolling calls to collaborating instances together under a single (coarser) interface. This wouldn't normally be a done to hide which interfaces a class supports. Indeed, publishing which interfaces a class extends is important to API users. You could roll the three interfaces into a single interface for "convenience" but I think this is unnecessary. If you do settle on a common superclass then that would "extend" the three interfaces (if all subclasses were expected to support them). It may also implement some default implementation of these interfaces (again, watch access modifiers to ensure those you intend to be can be overridden while others may be final).
Edit: Guidance
You just have to identify the classes and relationships:
I suggest you just grab some paper and draw. You already have your nouns and verbs (you can otherwise go noun and verb spotting to identify classes and methods on them).
So, why not draw a simple diagram containing all the info (A, B, C, Main, etc) and draw the relationships between them. This is your start point. You may have some confusion when working out how Main links to the window classes (given there are two kinds). Just write a note on it and move on to clarify the rest of the picture.
Next, refine your diagram to start moving common features into a single place (abstraction). You know this exists with regards to your interfaces and the methods you suggest but you may need to decide which (if any) have any common functionality. Then decide if interfaces satisfies your needs (methods are common but implementations are different) or if the implementation itself is the same and so a parent superclass may be useful (this addresses abstraction [who is responsible for what], encapsulation [individual implementations at the appropriate level] and polymorphism [which classes support common methods]). Note that, even if you settle on an superclass, you'd be wise to back it with an interface (it makes introduction of sibling or replacement classes easier in time - think maintenance).
Next, work on the issues you found. Has your draft design clarified any of them? For instance, your Main needs to know about its windows but - what type are they? So, has any of your refinement made this clearer?
Do any patterns present themselves? for this you need to already have a feel for design patterns I'm afraid so buy and absorb the GoF Design Patterns book. It'll put you in good stead for spotting patterns as you go. I'd also recommend reading this specific book before taking on any others as it's technology agnostic (and some other books arebloated with tech-specific workarounds). Perhaps study the two patterns I pointed out and see if they fit your requirement.
On the whole though, your ideas seem to be going in the right direction.
Not sure if the title captures what I'm trying to say here.
When designing in OO should I be splitting my objects up into their most specific areas - so if I have a factory object that deals with creating objects but later on i come across a way of creating objects for another purpose even though they may be the same objects is it worth creating a seperate fcatory or just add to the exsiting.
My biggest worry is bulking up classes with tons of stuff, or splitting objects and diluting my projects into a sea of classes.
Any help?
EDIT:
I guess on a side note/sub topic part of me wants to find out the level of granularity you should use in a program. Kind of, how low should you go?
My biggest worry is bulking up classes with tons of stuff, or
splitting objects and diluting my
projects into a sea of classes
This is a very valid point and in any even reasonably sized project, extremely difficult to get right up front especially because realistically, requirements themselves evolve over time in most cases.
This is where "Refactoring" come in. You design based on what you know at any given point and try not too make too many leaps of faith as to what you think the system MAY evolve to.
Given that you know what you are building right now, you design your classes trying to make the best possible use of OO concepts - eg encapsulation / polymorphism. This is itself, like others have noted as well, can be notoriously difficult to achieve and thats where experience, both in designing OO systems as well as knowledge of the domain can really come in handy.
Design based on what you know --> Build It --> Review it --> Refactor it --> Re-design --> and it goes on and on..
Finding the correct level of detail and responsibility is what makes OOP design so difficult. We can help you with a specific case but not with anything this general. If there were algorithms or strict methodologies of how to solve this, everyone could be an OOP designer.
A rule of thumb I like for deciding "is this getting too big now?" is "can I explain the purpose of it concisely?" If you start having to introduce caveats and lots of weasel words to explain the functions of a component of your design (be it class, member variable, method or whatever) it might be a good indicator that it's getting too complex and should be split up.
In your specific case, if you already have a factory object then the DRY Principle (Don't Repeat Yourself) would say that it's a bad idea to create another factory that does the same thing.
Is this an actual problem that you face? Or merely a fear about how your code might grow in the future?
If you are using the same type of object to solve drastically different problems then you may need to redesign the class to focus on seperation of concerns. If you need a more specific answer, you will need to provide an example of a type of class that would need this functionality.
I might have worded things badly in
the Q. I guess I wouldnt be repeating
myself its just more of a case of
where to put the code, it could be
added to an exsiting factory that
creates design objects for exporing
data to excel spreadsheets. On the
other hand I could see it could also
have its own factory for importing
excel data. Both factories would
produce the same objects but the inner
workings are completely different. –
If you aren't doing or plan on doing any class abstraction (subclassing or using interfaces) you may not need to use the factory pattern at all. The factory pattern is generally best suited for supplying objects of a base class type or that implement a specific interface.
Both
factories would produce the same
objects but the inner workings are
completely different.
Not sure if I've understood you correctly, but this sounds like a candidate for the AbstractFactory pattern.
We are following Domain Driven Design for the implementation of a large website.
However by putting the behaviour on the domain objects we are ending up with some very large classes.
For example on our WebsiteUser object, we have many many methods - e.g. dealing with passwords, order history, refunds, customer segmentation. All of these methods are directly related to the user. Many of these methods delegate internally to other child object but
this still results in some very large classes.
I'm keen to avoid exposing lots of child objects
e.g. user.getOrderHistory().getLatestOrder().
What other strategies can be used to avoid this problems?
The issues you are seeing aren't caused by Domain Driven Design, but rather by a lack of separation of concerns. Domain Driven Design isn't just about placing data and behavior together.
The first thing I would recommend is taking a day or so and reading Domain Driven Design Quickly available as a free download from Info-Q. This will provide an overview of the different types of domain objects: entities, value objects, services, repositories, and factories.
The second thing I would recommend is to go read up on the Single Responsibility Principle.
The third thing I would recommend is that you begin to immerse yourself in Test Driven Development. While learning to design by writing tests first won't necessarily make you designs great, they tend to guide you toward loosely coupled designs and reveal design issues earlier.
In the example you provided, WebsiteUser definitely has way too many responsibilities. In fact, you may not have a need for WebsiteUser at all as users are generally represented by an ISecurityPrincipal.
It's a bit hard to suggest exactly how you should approach your design given the lack of business context, but I would first recommend doing some brain-storming by creating some index cards representing each of the major nouns you have in your system (e.g. Customer, Order, Receipt, Product, etc.). Write down candidate class names at the top, what responsibilities you feel are inherent to the class off to the left, and the classes it will collaborate with to the right. If some behavior doesn't feel like it belongs on any of the objects, it's probably a good service candidate (i.e. AuthenticationService). Spread the cards out on the table with your colleges and discuss. Don't make too much of this though, as this is really only intended as a brainstorming design exercise. It can be a little easier to do this at times than using a whiteboard because you can move things around.
Long term, you should really pick up the book Domain Driven Design by Eric Evans. It's a big read, but well worth your time. I'd also recommend you pick up either
Agile Software Development, Principles, Patterns, and Practices or Agile Principles, Patterns, and Practices in C# depending on your language preference.
Although real humans have lots of responsibilities, you're heading towards the God object anti-pattern.
As others have hinted, you should extract those responsibilities into separate Repositories and/or Domain Services. E.g.:
SecurityService.Authenticate(credentials, customer)
OrderRepository.GetOrderHistoryFor(Customer)
RefundsService.StartRefundProcess(order)
Be specific with naming conventions (i.e. use OrderRepository or OrderService, instead of OrderManager)
You've run into this problem because of convenience. i.e. it's convenient to treat a WebsiteUser as an aggregate root, and to access everything through it.
If you place more emphasis on clarity instead of convenience, it should help separate these concerns. Unfortunately, it does mean that team members must now be aware of the new Services.
Another way to think of it: just as Entities shouldn't perform their own persistence (which is why we use Repositories), your WebsiteUser should not handle Refunds/Segmentation/etc.
Hope that helps!
A very simple rule of thumb to follow is "most of the methods in your class HAVE to use most of the instance variables in your class" - if you follow this rule the classes will be automatically of the right size.
I ran into the same problem, and I found that using child "manager" objects was the best solution in our case.
For example, in your case, you might have:
User u = ...;
OrderHistoryManager histMan = user.getOrderHistoryManager();
Then you can use the histMan for anything you want. Obviously you thought of this, but I don't know why you want to avoid it. It seperates concerns when you have objects which seem to do too much.
Think about it this way. If you had a "Human" object, and you had to implement the chew() method. Would you put it on the Human object or the Mouth child object.
You may want to consider inversing some things. For example, a Customer doesn't need to have an Order property (or a history of orders) - you can leave those out of the Customer class. So instead of
public void doSomethingWithOrders(Customer customer, Calendar from, Calendar to) {
List = customer.getOrders(from, to);
for (Order order : orders) {
order.doSomething();
}
}
you could instead do:
public void doSomethingWithOrders(Customer customer, Calendar from, Calendar to) {
List = orderService.getOrders(customer, from, to);
for (Order order : orders) {
order.doSomething();
}
}
This is 'looser' coupling, but still you can get all the orders belonging to a customer. I'm sure there's smarter people than me that have the right names and links referring to the above.
I believe that your problem is actually related to Bounded Contexts. For what I see, "dealing with passwords, order history, refunds, customer segmentation", each one of these can be a bounded context. Therefore, you might consider splitting your WebsiteUser into multiple entities, each one corresponding to a context. There may arise some duplication, but you gain focus on your domain and get rid off very large classes with multiple responsibilities.
As a project over summer while I have some downtime from Uni I am going to build a monopoly game. This question is more about the general idea of the problem however, rather than the specific task I'm trying to carry out.
I decided to build this with a bottom up approach, creating just movement around a forty space board and then moving on to interaction with spaces. I realised that I was quite unsure of the best way of proceeding with this and I am torn between two design ideas:
Giving every space its own object, all sub-classes of a Space object so the interaction can be defined by the space object itself. I could do this by implementing different land() methods for each type of space.
Only giving the Properties and Utilities (as each property has unique features) objects and creating methods for dealing with the buying/renting etc in the main class of the program (or Board as I'm calling it). Spaces like go and super tax could be implemented by a small set of conditionals checking to see if player is on a special space.
Option 1 is obviously the OO (and I feel the correct) way of doing things but I'd like to only have to handle user interaction from the programs main class. In other words, I don't want the space objects to be interacting with the player.
Why? Errr. A lot of the coding I've done thus far has had this simplicity but I'm not sure if this is a pipe dream or not for larger projects. Should I really be handling user interaction in an entirely separate class?
As you can see I am quite confused about this situation. Is there some way round this? And, does anyone have any advice on practical OO design that could help in general?
EDIT: Just like to note that I feel I lost a little focus on this question. I am interested in the general methodology of combining OO and any external action(command line, networking, GUI, file management etc) really.
In the end, it is up to you. That is the beauty of OO, in that it is subject to interpretation. There are some patterns that should usually be adhered to, but in general it is your decision how to approach it.
However, you should carefully consider what each actor in the system should know about the rest of it. Should a property really know about the player, his account balance, and the other players? Probably not. A property should know what it costs, how much its rent is, etc.
On the other hand, should the main playing thread be concerned about trivial matters such as paying rent? Probably not. Its main concern should be the state of the game itself, such as dice rolling, whether each player wants to trade or buy or unmortgage/mortgage, things like that.
Think for a moment about the action of landing on a square. Once landed, the player has 3 options:
Buy the property
Ignore the property
Pay rent
Now, which actor in the system knows all the information required to complete that. We have the Game class, which isn't concerned with such tedium. We have the Property, which doesn't really care about the players. But the Player object knows all this information. It keeps a record of what each player owns, and can easily access the proper data.
So, if it were me, I would make a Player.performMove(Die d) method. It has easy access to the accounts. This also allows for the least coupling among classes.
But in the end, it's up to you. I'm sure people have created Monopoly clones in perfect OO, as well as Functional or Procedural languages too. In the end, use what you know and keep refactoring until you're happy with the end design.
I agree option #1 seems better.
As for "user interaction" - it all depends. You could leave some of your code in another class. For example,
// in main class
user.landOn(space);
if (space.containsProperties()) doSomething(); // Option #1 for some user-interaction code
// in User.java
public void landOn(Space s) {
// do some checks
s.land(this);
if (s.containsProperties()) {...} // Option #2
// something else?
}
// in GetMoneySpace.java
#Override
public void land(User u) {
u.awardCash(200);
// Option #3 - no properties so nothing here
}
This is far more OOP-y (and better, in my opinion) than something like
if (space.isCashAwardSpace()) {
user.awardCash(space.getAward());
}
if (user.something()) doSomething(); // Some user-interaction code
I am not entirely sure if I understand it correctly. You have always such choice when designing software. I would personally go for the first choice. One argument is personal experience with small games (Scrabble), which prooved to me that good design matters for smaller projects as well. The point of OOP is that you can think differently about your design and you get some design benefits. For example imagine how hard it will be to add new field, change existing one, reuse behaviour of one field in multiple fields.
Your first approach is the one I'd go for. It encapsulates the behaviour where it's needed. So, you'd have Space subclasses for Utilities, Properties, GotoJail, FreeParking - basically all the different cateogires of spaces. What groups a category is it's common behaviour.
Your properties spaces may themselves have a group object as a member, e.g. to group all the dark blue properties together.
As to interaction with the user, you pass a Board (or better a GameController) instance to each space, so it knows which Game it is part of and can influence the game. The Space can then invoke specific actions on the board, such as, moving a piece, asking the user a question etc. The main point is that there is separation - the user interaction is not happening inside each Space - but the space is allowed to request that some interaction happens, or that a piece is moved. It's up to your GameController to actually do the interaction or move pieces etc. This separation makes it easy to test, and also provide alternative implementations as the need may arise (E.g. different game rules in different editions/countries?)
Go with the first design. You'd have a Property class, and subclass the special properties, overriding the default behavior.
As far as interaction, you could have a Token class, and move an instance of that around the board. You have to give the user some options, but yes, from the responses, you should be calling methods on objects, not putting complex logic in the user events.
Sample classes:
Property
name
price
baseRent
houseCount
hotelCount
mortgaged
getCurrentRent()
RailRoad extends Property
Utility extends Property
Board
properties
User
token
playerName
currentProperty
ownedProperties
buyProperty()
payRentOnProperty()
mortgageProperty()
move()
Option 2 doesn't make much sense, or at least it's not as clear to me as option 1. With option 1 you don't need to handle user interaction inside your space object. You could have in your main class or a separate class dedicated to handle user interaction:
public void move(Player p, int spaces){
Space landingSpace = board.getLandingSpace(p,spaces);
landingSpace.land(p); //apply your logic here
}
As you can see, the Space class is responsible for checking the Player p that intends to land on that space. It applies any custom logic, checks if it has enough money, if it's something that the player owns, etc. Each subclass of Space will have its own set of rules, as you described in option 1.
Part of the point of object-oriented design is to simplify the representation of the problem within the solution space (i.e., modeling the system in the computer). In this case, consider the relationships between objects. Is there enough functionality in a Space to warrant abstracting that into a class, or does it make more sense for there to be discrete Property and Utility classes unrelated to Space because of the unique features of both? Is a Property a special kind of Space, or merely a field within Space? These are the kinds of problems you probably will need to grapple with in designing the game.
As far as interaction, it's generally bad news for a design when you have a 'god class' that does all the work and merely asks the other classes for information. There are plenty of ways to fall into this trap; one way to determine whether you are dealing with a god class is to look for a class name including Manager or System. Thus, it's probably not the best idea to have some sort of "game manager" that asks all the other objects for data, makes all the changes, and keeps track of everything. Eliminate these as much as possible.
God classes violate the concept of encapsulation, which involves more than data hiding (though that's certainly a big part of it). Good encapsulation means that related methods and data are part of a single object. For example, a Property doesn't need to make requests of its owner, so a field containing a reference to the Player could violate encapsulation. Some of these encapsulation violations aren't obvious at all, and can be hard to spot. When designing the object, try to determine the smallest amount of information about the object that needs to be shared with external objects. Trim out anything unnecessary.
You can obviously go about this in a lot of ways, but my design would be something like this (iteration could certainly prove it wrong):
Space class that contains basic data and methods that are common to all spaces (such as their position on the board, occupied or not, etc.).
Subclasses moving from most common (Property and Utility) to most unique (Go, Jail, FreeParking, and so on; probably singletons) with fields and methods related to each.
Player class to contain player information.
GameState class that is concerned with game state; whose turn it is, how many houses are left in the bank, and so on.
Good luck with the game and your continued studies.
Naturally, Google is your friend, but here's a sampling of things I would recommend reading:
ATM simulation (this idea is
also discussed in Rebecca
Wirfs-Brock's book below)
Object-Oriented Design Heuristics - Arthur Riel
How Designs Differ (PDF), Designing Object-Oriented Software - Rebecca Wirfs-Brock