领域模型类应该总是依赖于原语吗?

Ram*_*mon 17 python architecture oop domain-driven-design repository-pattern

进行到一半时Architecture Patterns with Python,我有两个关于应该如何构造和实例化领域模型类的问题。假设在我的领域模型上我有这样的课程DepthMap

class DepthMap:
    def __init__(self, map: np.ndarray):
        self.map = map
Run Code Online (Sandbox Code Playgroud)

根据我从书中的理解,这个类是不正确的,因为它依赖于Numpy,它应该只依赖于 Python 原语,因此问题是:域模型类应该只依赖于 Python 原语,还是有例外?

假设上一个问题的答案是类应该完全依赖于基元,那么从 Numpy 数组创建 DepthMap 的正确方法是什么?假设现在我有更多格式可以制作 DepthMap 对象。

class DepthMap:
    def __init__(self, map: List):
        self.map = map
    
    @classmethod
    def from_numpy(cls, map: np.ndarray):
        return cls(map.tolist())

    @classmethod
    def from_str(cls, map: str):
        return cls([float(i) for i in s.split(',')])
Run Code Online (Sandbox Code Playgroud)

或工厂:

class DepthMapFactory:
    @staticmethod
    def from_numpy(map: np.ndarray):
        return DepthMap(map.tolist())

    @staticmethod
    def from_str(map: str):
        return DepthMap([float(i) for i in s.split(',')])
Run Code Online (Sandbox Code Playgroud)

我认为即使是Repository Pattern他们在书中经历的 ,也可以放在此处:

class StrRepository:
    def get(map: str):
        return DepthMap([float(i) for i in s.split(',')])

class NumpyRepository:
    def get(map: np.ndarray):
        return DepthMap(map.tolist())

Run Code Online (Sandbox Code Playgroud)

第二个问题:从不同来源创建域模型对象时,正确的做法是什么?

注意:我的背景不是软件;因此一些 OOP 概念可能是不正确的。请发表评论,让我知道如何改进问题,而不是投反对票。

小智 13

我写了这本书,所以我至少可以试一试在回答你的问题。

您可以在域模型中使用基元(str、int、boolean 等)以外的东西。通常,虽然我们无法在书中展示它,但您的模型类将包含对象的整个层次结构。

您想要避免的是您的技术实现以一种难以表达您的意图的方式泄漏到您的代码中。除非您的域是 Numpy,否则在您的代码库周围传递 Numpy 数组的实例可能是不合适的。我们试图通过将有趣的东西与胶水分开来使代码更易于阅读和测试。

为此,您可以使用一个 DepthMap 类来公开某些行为,并且恰好有一个 Numpy 数组作为其内部存储。这与使用库中的任何其他数据结构没有什么不同。

如果您将数据作为平面文件或其他东西,并且创建 Numpy 数组涉及复杂的逻辑,那么我认为 Factory 是合适的。这样你就可以在你的系统边缘和模型之外保留用于生成深度图的无聊、丑陋的代码。

如果从字符串创建 DepthMap 真的是单行的,那么 classmethod 可能更好,因为它更容易找到和理解。


pla*_*alx 8

我认为依赖纯语言扩展的库是完全没问题的,否则你最终将不得不定义大量的“接口契约”(Python 没有将接口作为语言构造——但这些可能是概念性的)抽象掉这些数据结构,最终那些新引入的契约无论如何都可能是糟糕的抽象,只会导致额外的复杂性。

这意味着您的域对象通常可以依赖于这些纯类型。另一方面,我也认为这些类型应该被视为语言“原语”(本机可能更准确),就像datetime你想避免原语痴迷一样

换句话说,DepthMap允许哪个领域概念依赖于Numpy它的构造(这里不需要抽象),但Numpy不一定允许深入领域(除非它是适当的抽象)。

或者在伪代码中,这可能很糟糕:

someOperation(Numpy: depthMap);
Run Code Online (Sandbox Code Playgroud)

这可能更好的地方:

class DepthMap(Numpy: data);
someOperation(DepthMap depthMap);
Run Code Online (Sandbox Code Playgroud)

关于第二个问题,从 DDD 的角度来看,如果 DepthMap 类有一个 Numpy 数组作为它的内部结构,但必须从其他来源(例如字符串或列表)构造,最好的方法是存储库模式吗?或者这只是为了处理数据库而工厂是更好的方法?

Repository 模式专门用于存储/检索,因此它不合适。现在,您可能有一个直接DepthMap接受 a的工厂方法,Numpy或者您可能有一个专门的工厂。如果你想解耦DepthMapNumpy那么它可能是有意义的引入专门的工厂,但它乍看起来不必要在这里。