mardi 22 octobre 2019

Is this the correct implementation of factory pattern?

First of all, this is not a homework. Secondly, it is a bit long. Sorry and thank you for your patience in advance.

There is a.. game, basically it provides you a starting condition, a desired result, a bunch of process and maximum number of process. The job is to find the correct order of executing process in order to get to the desired result.
So I think it might be good to use computer to solve the problem. The maximum step is usually under 10 so it should be safe to use brute force.

Here is my code for the solution(btw it is in python). To simply put it, I create an object for each process and put them into a list. In every step I iterate through the list, pass the parameters(starting condition, expected result, etc.) to the process object and check if the output matches the expected result. If it matches, it returns the stack of process being called.

class Puzzle:
    def __init__(self, initial_num, num_steps, result, list_process):
        self.initial_num = initial_num
        self.num_steps = num_steps
        self.result = result
        self.list_process = list_proces

    def solve(self):
        for process in self.list_process:
            stack = process.run(self.initial_num, self.num_steps, self.result, self.list_process)
            if stack is not None:
                return stack
        return None

For the processes, because there are many type of processes and in each case there are different combination of processes to execute. I happened to use a bit of Java so I use the idea of polymorphism which is not really needed in python but I think it is a neat structure.

class ProcessBase:

    @property
    def name(self):
        print("need to rewrite")

    def do_process(self, num_input):
        print("need to rewrite")

    def run(self, input_, steps_remain, result, processes_next):
        if steps_remain is 0:
            return None
        try:
            output= self.do_process(input_)
        except RuntimeError:
            return None
        if output == result:
            stack = [self.name]
            return stack
        for process in processes_next:
            stack = process.run(output, steps_remain - 1, result, processes_next)
            if stack is not None:
                stack.append(self.name)
                return stack
        return None

class ProcessA(ProcessBase):

    def __init__(self, param):
        super().__init__()
        self.param = param

    @property
    def name(self):
        return "ProcessA" + self.param

    def do_process(self, input):
        # do the process
        return output

So basically the run() is the same in every process, so I only need to rewrite the name() for displaying the call stack, and do_process() for the actual process. Then my factory method(It is not a class because I don't think there is need for multiple factories) is as below.

def process_factory(str_input):
    if str_input=="A":
        return ProcessA()
    elif str_input=="B":
        return ProcessB()
    # etc

So in actually using it, I can leave most of the code unchanged. When there is a new type of process, I only need to add a new class, overwrite the name() and do_process(), add a couple of lines in process_factory() method and it is good to go. However I am still not confident about it. My question(finally!) is:

  1. Is this implementation all right, or even excessive in Python?
  2. In the process_factory() method, the "if...elif..." is getting pretty long, I was thinking of making it as a dictionary. like below:

    methods = {'A': ProcessA(), 'B': ProcessB() }

    But it means that all of the objects will be created even if it is not used. I could also replace the constructors in the dict into a method which returns an object, but I need to write more lines when I need to add another type of Process. Is there a better way?

  3. After a while, there is a new type of process which does not change the input, but change the way processes behave afterwards, which is not really compatible to my current design.
    My solution is:

    • Add a new Process class and rewrite the run() method as well. It did least change to the original code but it kind of defies the idea of the original structure.
    • Change the entire logic so that a process does not only affect the input but also affect other parameters(such as processes available, even result). But there will be great deal of changes and I need to think about how to maintain the old copy of process while create a new altered version of process.

    What do you think should be the best solution?

Aucun commentaire:

Enregistrer un commentaire