Предположим, у меня есть два таких класса, которые определяют игру с угадыванием чисел и агента, который будет играть в эту игру:
import random
class GuessGame:
def __init__(self, controller, n = 100):
self.controller = controller
self.n = n
def run(self):
number = random.randrange(self.n)
self.interact(number)
def interact(self, number):
self.controller.inform_n(self.n)
while True:
guess = self.controller.get_guess()
if guess == number:
self.controller.inform_result(0)
return
elif guess < number:
self.controller.inform_result(-1)
else:
self.controller.inform_result(1)
class GuessAgent:
def __init__(self, environment, n = 100):
self.environment = environment
self.n = n
def run(self):
x0 = 0
x1 = self.n
while True:
x = (x0 + x1) // 2
result = self.environment.guess(x)
if result == 0:
return
elif result == -1:
x0 = x + 1 # Inclusive lower bound
else:
x1 = x # Exclusive upper bound
if __name__ == '__main__':
class ManualController:
def inform_n(self, n):
print(f'n = {n}')
def inform_result(self, result):
print({0: 'Correct', -1: 'Too low', 1: 'Too high'}[result])
def get_guess(self):
return int(input())
class ManualEnvironment:
def guess(self, n):
print(f'Guess: {n}')
print('0 = Correct, -1 = Too low, 1 = Too high')
return int(input())
GuessGame(ManualController()).run()
GuessAgent(ManualEnvironment()).run()
Как видно, они оба отлично работают со своими собственными интерфейсами. Проблема в том, что они оба хотят быть процедурой верхнего уровня, которая вызывает другую сторону, поэтому нет очевидного способа соединить их. В случае с реальным миром оба класса довольно сложны (и могут быть написаны кем-то другим), поэтому «просто переписать один из них» - нежелательный ответ.
Решения, которые я могу придумать являются:
- Сделать их отдельными подпроцессами. Это работает, но мне кажется слишком жестким подходом ко мне. Я только пытаюсь запустить симуляцию, и я боюсь, что общение на уровне ОС потребует слишком много накладных расходов.
- Сделайте одну из них функцией генератора. Помимо того, что я не очень интуитивно понятен, самая большая проблема, с которой я сталкиваюсь при таком подходе, заключается в том, что некоторые операторы коммуникации довольно глубоко скрыты в дереве вызовов (я сделал
GuessGame.interact
отдельной функцией, чтобы проиллюстрировать это), и мне нужно изменить все функции в цепочке вызовов для функций генератора, чтобы сделать эту работу. Было бы приемлемо, если бы мне нужно было только изменить некоторый код в точке операторов связи и, возможно, в точках входа, но все в цепочке вызовов слишком много.
Есть ли более элегантный шаблон для решения такого рода проблем?