سؤال

When I do my code layout, I like to start with a rather high level view, and then start to delegate actual computation to class or functions that have a bit more detail. Then in these classes of functions, I do the same, layer by layer, until I get to the bottom where I have to do the 'real' action.

Example:

def build_table():
    legs = get_legs()
    plate = get_plate()
    return put_together(legs, plate)

def get_legs():
    legs = []
    for i in [0,1,2,3]:
        legs.append(get_leg_from_warehouse())
    return legs

def get_plate():
    plate = get_plate_from_warehouse()
    return finish_plate(plate)

def put_together(legs, plate):
    table = Table()
    for i in [0,1,2,3]:
       table.mount(legs[i])
    table.mount(plate)
    return table

class Table:
    self.component = []
    def mount(self, item):
        self.component.append(item)

In this way, I find it easy to think about the layout, and hide complexity. I mostly have short pieces of code that are easy to understand.

The downside of this is, that when I discover I need a piece of information available at the top of the stack further down, I pass arguments from function to function. In the above example, I might gather 'screws' at the top, and then keep passing them down to a function where they are actually drilled into the wood. This makes it feel that it is not so easy to modify the code, and I wondered what I could do about this. In this example the modified code would look like this:

def build_table():
    legs = get_legs()
    plate = get_plate()
    screws = get_screws()
    return put_together(legs, plate, screws)

def get_legs():
    legs = []
    for i in [0,1,2,3]:
        legs.append(get_leg_from_warehouse())
    return legs

def get_plate():
    plate = get_plate_from_warehouse()
    return finish_plate(plate)

def get_screws():
    drive_to_hardwarestore()
    screws = buy_screws()
    drive_home()
    return screws

def put_together(legs, plate, screws):
    table = Table()
    for i in [0,1,2,3]:
       table.mount(legs[i], screws)
    table.mount(plate, screws)
    return table

class Table:
    self.component = []
    def mount(self, item, screws):
        self.component.append((item, screws.pop()))

So besides adding the code for getting screws, I had to modify 4 lines. This would increase linearly with the amount of layers.

How can I refactor? On the other hand, how can I avoid this in the first place? Is my design process 'wrong'?

هل كانت مفيدة؟

المحلول

So besides adding the code for getting screws, I had to modify 4 lines. This would increase linearly with the amount of layers.

I think this is a fallacy. When you add another kind of table part, you will still have only to change 4 lines of code in your "object construction layer". The layers above the "object construction" layer in your code will just work with "table" objects, unmodified, as long as they don't have to deal with the new kind of parts. You can pass that table object through an arbitrary number of layers and don't have to change anything. And when you find a part in your code where you have to access your screws, that code can just ask the "table object" for the screws.

Of course, when the object construction process itself gets more complicated, and you think you have "too much passing around" in there, you may consider to create a (table) factory class to encapsulate the constructions. That way, it is possible to create "screws", "legs", and "plate" in one place, store them in member variables of the factory, and get them later in the "mount" function.

So as long as the construction process is simple, stick to your design, and if it gets more complicated, use factory classes.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى softwareengineering.stackexchange
scroll top