Python OOP advice
Paul McGuire
ptmcg at austin.rr.com
Wed Sep 17 08:57:46 EDT 2008
On Sep 17, 6:50 am, Simon Hibbs <simon.hi... at gmail.com> wrote:
> I'm rewriting a design application for a science fiction game. in it
> you design your own starships. Each component has a mass and cost, but
> the mass can either be fixed or it can be expressed as a percentage of
> the tonnage of the overall ship.
>
> Orriginaly I thought I'd need to have a hull object which contains
> component objects, but the component objects need access to members of
> the hull object (e.g. the hull size) so that looks messy to implement.
>
I would not put this kind of intelligence into the components.
I think the issue here is that your Ship container is not really just
a generic container of ship components, but an assembly with some
specific requirements (must have 1 and only 1 hull, must have 1 or
more engines, etc.) and structure. I would create a class called
ShipDesign that had specific members for those components that have
special logic attached to them, and then more generic list members for
collection-ish components.
Since the hull is such a significant constraint, I would make it an
initialization argument. I would also put some kind of property on
hull representing its "capacity" (oh I see, you have something call
hull_size).
One way to generalize the fixed-cost vs. percentage-cost components
would be to have all components implement a compute_load function that
takes the ShipDesign as an argument. Those that are fixed-cost simply
return their fixed value, those that are percentage-cost can return
their percentage of the ShipDesign's hull.hull_size - this leaves the
door open for other variations on load, that could be based on other
properties besides the hull size.
Here's how I envision your ShipDesign class:
class ShipDesign(object):
def __init__(self, hull):
self.hull = hull
self.engines = []
self.shields = []
self.weapons = []
self.other = []
def compute_consumed_capacity(self):
load = 0
for itemlist in (self.engines, self.shields,
self.weapons, self.other)):
load += sum(item.compute_load(self)
for item in itemlist)
return load
def add_engine(self,e):
engload = e.compute_load(self)
if engload + self.compute_consumed_capacity() >
self.hull.hull_size:
raise ExceededHullCapacityException()
if len(self.engines) == MAXIMUM_ALLOWED_ENGINES:
raise ExceededMaximumConfigurationLimitException()
self.engines.append(e)
def set_hull(self, hull):
if self.compute_consumed_capacity() <= hull.hull_size:
self.hull = hull
else:
raise NewHullTooSmallException()
def is_valid(self):
if not self.engines:
raise InvalidDesignException("must have at least 1
engine")
...etc...
class GenericItem(object):
def __init__(self, name, load):
self.name = name
self.load = load
crewQuarters = GenericItem("Crew Quarters", 50)
disco = GenericItem("Discotheque", 10)
...etc...
Once you have a valid ShipDesign, you can then use it to construct
multiple Ship instances.
Here is how I would work around your "only one hull at a time"
problem. Define several classes for different kinds of hulls:
class CheapHull(Hull):
capacity = 100
name = "EconoHull 1000"
class MediumHull(Hull):
capacity = 500
name = "Mainliner X50"
class TopOTheLineHull(Hull):
capacity = 1000
name = "LuxeMaster 5000"
and then create ship designs with a CheapHull, a MediumHull, or a
TopOTheLineHull. In this case, the member variable of the ShipDesign
is really a class, which you would later use to construct hull
instances as part of making Ship instances from your ShipDesign.
class Ship(object):
def __init__(self, design):
self.hull = design.hull()
self.engines = design.engines[:]
...etc...
This way, each Ship will have its own Hull instance, so that you
can track instance-specific properties, such as damage percentage.
If you don't want to hard-code the hull types, then you can do
something similar with instances of a generic Hull class, which you
would then use as prototypes when constructing Ship instances. Just
be careful that you don't accidentally have all ships sharing the same
hull instance!
-- Paul
More information about the Python-list
mailing list