[英]What's the best way to design a class that calls sequences of its methods?
I have a class similar to the one below where it goes through a series of methods using variables in the class. The code used to be a massive series of functions, passing variables around and this felt more structured and easy to work with/test.我有一个 class 类似于下面的代码,它通过一系列使用 class 中的变量的方法。代码曾经是大量的函数,传递变量,这感觉更有条理并且易于使用/测试。 However, it still feels as though there's a better way.但是,仍然感觉好像有更好的方法。
Is there a better design pattern or approach for situations like this?对于这种情况,是否有更好的设计模式或方法? Or is going the object route a mistake?或者走 object 路线是错误的吗?
In terms of testing process()
and other methods, I can just mock the methods called and assert_called_once
.在测试process()
和其他方法方面,我可以模拟 called 和assert_called_once
方法。 But, ultimately, it leads to ugly testing code with tons of mocks.但是,最终,它会导致带有大量模拟的丑陋测试代码。 So, it makes me wonder about question #1 again.所以,这让我再次想知道问题 #1。
class Analyzer:
def __init__(self):
self.a = None
self.b = None
def process(self):
self.gather_data()
self.build_analysis()
self.calc_a()
self.calc_b()
self.build_output()
self.export_data()
...
def gather_data(self):
self.get_a()
self.get_b()
self.get_c()
...
def build_analysis(self):
self.do_d()
self.do_e()
self.do_f
...
As for testing, and I know this code isn't technically right, but I just wanted to illustrate how it gets hard to read/sloppy.至于测试,我知道这段代码在技术上是不正确的,但我只是想说明它是如何变得难以阅读/草率的。
class TestAnalyzer:
@patch.object(Analyzer, 'gather_data')
@patch.object(Analyzer, 'build_analysis')
@patch.object(Analyzer, 'calc_a')
@patch.object(Analyzer, 'calc_b')
@patch.object(Analyzer, 'build_output')
@patch.object(Analyzer, 'export_data')
def test_process(self, m_gather_data, m_build_analysis, m_calc_a,
m_calc_b, m_build_output, m_export_data):
analyzer.process()
m_gather_data.assert_called_once()
m_build_analysis.assert_called_once()
m_calc_a.assert_called_once()
...
Any insight or thoughts would be appreciated.任何见解或想法将不胜感激。 Thank you!谢谢!
Maybe the information expert design principle can help you或许信息专家设计原理可以帮到你
Assign responsibility to the class that has the information needed to fulfill it将职责分配给具有履行职责所需信息的 class
In your example it seems like you can split your class into different ones with better defined responsibilities.在您的示例中,您似乎可以将 class 拆分为具有更好定义的职责的不同部分。 Assuming that you have to perform the following functions in order:假设您必须按顺序执行以下功能:
I would create a class for each of them.我会为他们每个人创建一个 class。 Here you have some example code that generates some data and performs some basic calculations:这里有一些示例代码可以生成一些数据并执行一些基本计算:
from random import randint
from dataclasses import dataclass
@dataclass
class DataGatherer:
path: str
def get_data(self):
"""Generate fake x, y data"""
return randint(0, 1), randint(0, 1)
@dataclass
class Preprocessing:
x: int
y: int
def prep_x(self):
return self.x + 1
def prep_y(self):
return self.y + 2
@dataclass
class Calculator:
x: int
y: int
def calc(self):
return self.x + self.y
All I have done is to divide your code into blocks, and assigned methods and attributes to fullfill the functions of those blocks.我所做的只是将您的代码分成块,并分配方法和属性来完成这些块的功能。 Finally, you can create an Analysis
class whose only responsibility is to put the whole process together:最后,您可以创建一个Analysis
class,它的唯一职责就是将整个过程放在一起:
@dataclass
class Analysis:
data_path: str
def process(self, save_path: str):
x, y = DataGatherer(self.data_path).get_data()
print("Data gathered x =", x, "y =", y)
prep = Preprocessing(x, y)
x, y = prep.prep_x(), prep.prep_y()
print("After preprocessing x =", x, "y =", y)
calc = Calculator(x, y)
result = calc.calc()
print("Result =", result)
self.save(result, save_path)
def save(self, result, save_path: str):
print(f"saving result to {save_path}")
In the end this is all you have to do:最后这就是你所要做的:
>>> Analysis("data_path_here").process("save_path_here")
Data gathered x = 0 y = 1
After preprocessing x = 1 y = 3
Result = 4
saving result to save_path_here
When it comes to testing I use pytest .在测试方面,我使用pytest 。 You can create a test file for each of your classes (eg test_datagatherer.py, test_preprocessing.py...) and have a unit test function for each of your methods, for example:您可以为每个类创建一个测试文件(例如 test_datagatherer.py、test_preprocessing.py ...),并为每个方法进行单元测试 function,例如:
from your_module import Preprocessing
def test_prep_x():
prep = Preprocessing(1, 2)
assert type(prep.prep_x()) is int
def test_prep_y():
prep = Preprocessing(1, 2)
assert type(prep.prep_y()) is int
I will leave you to pytest documentation for more details.我将留给您 pytest 文档以获取更多详细信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.