1

Let's say I have an entity class ListOfItems with many related entities Item. These items are processed in a ItemsProcessor class that encapsulates the behavior. This class has several control lists and variable, for example, a processed_items list. Every time an item is done processing, it is added to that list:

class ItemsProcessor:
    def __init__(self, list_of_items):
        self.items = list_of_items.items
        self.processed_items = []

    def process(self):
        for item in self.items:
            if self._process_item(item):
                self.processed_items.append(item)

But now let's say we have a secondary class ItemsProcessorAlt that encapsulates additional processing. It needs a reference to the ItemsProcessor because it holds the state of the process, among other data that it uses:

class ItemsProcessorAlt:
    def __init__(self, processor):
        self.parent_processor = processor

    def process(self):
        for item in self.parent_processor.items:
            self._process_item_alt(item)

And this is how the main processor would use the alternative one:

class ItemProcessor:
    def process(self):
        # process all items as shown above
        if self._corrupt_items():
            subprocessor = ItemsProcessorAlt(self)
            subprocessor.process()

Now, when an item is processed by this last class, the state of the process needs to be updated, i.e. the processed_items list. Whose responsibility is it to update this state?:

  • Should ItemsProcessorAlt.process() update those lists directly?
  • Should ItemsProcessordefine a method to update the state and thenItemsProcessorAlt.process()` call it?
  • Should ItemsProcessorAlt.process() have a return value that hints the main processor how the state should be updated?
  • Should ItemsProcessorAlt set up a state on its own so that when the subprocess is finished the main processor updates its state based on that?

Note that the subprocessor is using variables that lie in the main processor and are changing as the process/subprocess is executed.

1

In the object oriented design an object should be responsible for and manage its own state. So the OO approach would be that each item has a process() method that knows how to process that item. Your ListOfItems could become an ItemCollection the goal here is to hide managing the enumeration from calling code, and then ItemCollection.ProcessItems() would be responsible for calling each item.process() and adding them to a processed items collection.

You can then use the strategy pattern to provide the correct process at the time you know it. If you know at creation of an item then the constructor/factory can ensure the correct process. If you don't know until you call ItemCollection.ProcessItems() you could add an argument to provide the proper process function or a function that can determine which to use. If you only need to call the alternate process if the first process fails in some way then you can provide the alternate process as a callback function or manage it directly in item.process().

For managing the state of items it would be the responsibility of item.process() to ensure that it either sets itself to the proper state, or returns a new object set to the proper state if immutability is desired. Then ItemCollection.ProcessItems() adds those items to processed_items which could be a property of ItemCollection or a separate object depending on you needs.

  • I agree on the item.process() part the problem is that actually the Item is a model class that simply stubs a db table, so it shouldn't know about any logic involving the process we're talking about. It'st just a "data class". – dabadaba Sep 14 '17 at 15:00
  • @dabadaba in that case Item should only be used directly in your data access, and the first thing you do wen you get data is to create BusinessItem which could make Item a member in the new object and house functions to keep you data classes pure. – Ryathal Sep 14 '17 at 17:13
  • so basically make a new class that wraps the data class with the sole purpose of its use in the context of this processing? – dabadaba Sep 14 '17 at 17:14
  • +1 for "In the object oriented design an object should be responsible for and manage its own state." This is obeying the OO priciple Tell-Don't-Ask while having an other object managing the state would violate it. – k3b Sep 15 '17 at 10:43
0

I'd split responsibilities in a Manager class, the operation's Context, and different Processor objects:

class Context:
    def __init__(self):
        self.processed_items = []
        self.some_variable1 = 0
        self.some_other_necessary_variable = True

class Processor1:
    def process(self, item, context):
        """
        Process an item and changes current context
        """
        self._do_stuff(item)
        self._update_context(context)

class Processor2:
    def process(self, item, context):
        """
        Process an item and changes current context
        """
        self._do_another_stuff(item)
        self._update_context(context)

class ItemProcessingManager:
    def __init__(self):
        self.context = Context();
        self.items = [] #list of available items
        self.processor1 = Processor1()
        self.processor2 = Processor2()
        #or: self.processors = [Processor1(), Processor2()]

    def process_items(self):
        for item in self.items:
            self.processor1.process(item, self.context)
            #...and if necessary
            if self._has_corrupt_item(item, self.context):
                self.processor2.process(item, self.context)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.