Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialStephen Hopkinson
4,648 PointsObjects accessing each other's attributes
I've just finished Kenneth's object-oriented programming course, and I'm trying to apply OOP techniques to creating an analogue clock. Unfortunately, I've run into some difficulties.
My model uses a separate instance of the Hand class for each hand on the clock face, plus a separate instance of the Clock class that handles all of the time updates (to avoid each hand having to separately call datetime.datetime.now() at each update).
Here's the problem: I need each Hand instance to reference the Clock instance in their update() method, but I don't know how to get objects to reference other objects' attributes.
In short: I need a way for hand.update() to access clock.now, but I don't know how to reference an object's attributes from another object. I'm sure I've missed something obvious, but I'm not sure what. Any tips much appreciated!
Stephen Hopkinson
4,648 PointsJust woken up and had an epiphany. Presumably the answer is to add 'time' as an argument in the hand class's get_position() method, and then pass in clock.hour (for example) in the main loop?
So hands.py would be updated as so:
def get_position(self, time):
self.x_position = # code to turn time into co-ordinates
self.y_position = # code to turn time into co-ordinates
And the main loop would be updated as so:
while True:
self.clock.update()
for hand in self.hands:
hand.get_position(clock.hour)
# code to draw clock
Would this be the best way to handle it?
2 Answers
Sang Han
1,257 PointsI would say that having your Hand
instances being able to call the update on your Clock
is probably not the best solution to the problem.
Instead, it's probably better for Main
to deal with datetime.datetime.now()
rather than Clock
.
When time changes, have your Main
deal with it and talk to Clock
which will update it's hand accordingly. Have Main
deal with drawing your clock by merely checking the attributes of the clock.
This is different than what you have now where your Main
class is composed of both a Clock
and Hands
which will mean that your Hands
will need to access Clock
. AClock
has Hands
, so naturally you want to have a Clock
which will delegate to Hand
Stephen Hopkinson
4,648 PointsThanks Sang, that makes sense. So presumably that means that OOP is best suited to hierarchical relationships between objects, rather than independent objects communicating with each other?
One question: is the difficulty of having objects accessing each other's attributes limited to Python's implementation, or is that true of all object-oriented languages?
Sang Han
1,257 PointsSorry, I didn't respond sooner!
But you're spot on, the idea that objects are individual entities kind of like people or things, that have a concrete representation of their "self" is a key OOP tenets. The idea that your hand object requires access to properties of the clock would mean that your clock doesn't fully contain the entirety of it's hands, and the hands are a separate entity that interacts with the clock on it's own.
Encapsulation
Kind of sounds abstract, but it's the key concept to the notion of Object Encapsulation, or information hiding. Well built objects are designed with the idea that you can hide an objects internal representation that can be swapped in and out, and other people only need to worry about the Interface to use your object (tidbit: Python uses duck-typing for creating interfaces, in other compiled oop languages interfaces are much more strict). This makes programming robust and code reusable. It's means that you don't need to know how to build a Shovel in order to show someone how to use one. Using good data hiding principles means you can take care of building a good Shovel class, and others only need to know how to use it's shovel.plot()
method.
Mechanisms of Delegation
Another key OOP principle is the notion of building more complex objects or specialized forms of a genral object that is otherwise known as Delegation. Delegation means that an object shall perform only what it knows best, and leave the rest to other objects. Delegation can be implemented with two different mechanisms: Composition or Inheritance.
Object Composition
You can create more complex/specialized object by simply composing it of other objects. Your new object will have it explicitly manage the work they do in order to maintain encapsulation. So in your code example you're using Delegation through object composition (although you may not have known that was what it's called) and trying to managing those explicit relationships is a topic where many design patterns have resulted in order to build robust systems.
Class Inheritance
The other way of doing delegation is through Class Inheritance. Inheritance is not as explicit as Composition, since the inherited object merely gains all the facilities and internal makeup of it's superclass. This can be a great tool for code reuse and extending functionality. It reduces a lot of boilerplate code without the need to design interconnections between objects. But as well, it also be a source of much frustration, especially in cases where tight control is needed over an objects makeup since the subclass has no control of the code it inherits. It can also be a case much confusion in languages like Python which support multiple inheritance, since it's can become hard to pinpoint which parent in the Class Hierarchy an object received which behavior.
This is where the whole "has-a" or "is-a" distinction comes to play. Generally if your object == "has-a" relationship use composition, whereas if an is related by object == "is-a" relationship, you want to use inheritance.
For instance, if here is an example of building a House you might build it like so.
class Door:
"""
Construct a pretty sweet door
so you can hide yo kids hide yo wives.
"""
def __init__(self, position='open'):
self.position = position
def close(self):
if not self.position == 'closed':
self.position = 'closed'
class House:
"""
Construct a house since being homeless isn't fun.
"""
def __init__(self):
self.door = Door()
self.locked = False
def lock(self):
self.door.close()
self.locked = True
def unlock(self):
if not self.door.position == 'closed':
raise WhoLeftTheDoorOpenError
self.locked = False
Obviously, since a house "has-a" door, rather than a house "is-a" door.
A case where you might rather use inheritance would be in the case you wanted to build different types of houses.
class Mansion(House):
"""
Because every mansion comes with a butler.
"""
def __init__(self, butler):
super(House, self).__init__(self)
self.butler = butler
def lock(self):
self.butler.get(self.door, 'locked')
def unlock(self):
self.butler.get(self.door, 'unlock')
There are tradeoffs when choosing between the two, and since the Python has inheritance built into the syntax, most people think it's the only way. But it's important to make the distinction because it makes your programs much easier to reason to follow the pattern and not have some weird behavior.
Usually composition is said to be a very generic technique that needs no special syntax, while inheritance and its rules are strongly dependent on the language of choice. Actually, the strong dynamic nature of Python softens the boundary line between the two techniques.
Stephen Hopkinson
4,648 PointsStephen Hopkinson
4,648 PointsHere's the code, if that helps. First, clock.py:
Then, hands.py:
And finally, main.py: