Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 使用新兴的业务需求条件清晰地构建可测试的模块化代码_Php_Design Patterns_Slim_Command Pattern_Chain Of Responsibility - Fatal编程技术网

Php 使用新兴的业务需求条件清晰地构建可测试的模块化代码

Php 使用新兴的业务需求条件清晰地构建可测试的模块化代码,php,design-patterns,slim,command-pattern,chain-of-responsibility,Php,Design Patterns,Slim,Command Pattern,Chain Of Responsibility,我正在尝试概念化为Yelp或FB签入类型系统编写代码的最清晰的方法,但是 还有一个签出组件。我正在使用PHP 基本要求可能是: 登记入住: 如果是“building”类型,我需要 通过UUID查找实际建筑,以获取其主键,我们将使用签入登录该主键,以便稍后将其拉入报告 (入住“大街123号”大楼) 当前无法将其签入任何内容 他们必须在房屋X米范围内 若他们不在房屋X米范围内,则必须记录异常字符串以解释原因 如果是“房间”类型,我需要 通过UUID查找实际房间以获取其主键 我们将使用签入登录,以便稍

我正在尝试概念化为Yelp或FB签入类型系统编写代码的最清晰的方法,但是 还有一个签出组件。我正在使用PHP

基本要求可能是: 登记入住: 如果是“building”类型,我需要

  • 通过UUID查找实际建筑,以获取其主键,我们将使用签入登录该主键,以便稍后将其拉入报告 (入住“大街123号”大楼)
  • 当前无法将其签入任何内容
  • 他们必须在房屋X米范围内
  • 若他们不在房屋X米范围内,则必须记录异常字符串以解释原因
  • 如果是“房间”类型,我需要

  • 通过UUID查找实际房间以获取其主键 我们将使用签入登录,以便稍后将它们拉入报告 (入住“123大街->212室”)
  • 他们已经需要被检查进一栋楼了
  • 他们已经不能入住房间了
  • 还有一个“退房”阶段,我提到这一点是为了让你明白,这并不像你刚才说的Yelp “我在这里”。登记入住并不是一个简单的动作;之后会有一个“退房”,我不在这里介绍 为了简洁起见。我的问题是如何创建更干净的控制器、方法和设计模式来构建 满足上述要求

    因此,请求发送json,点击(Slim)应用程序,转到方法的控制器(在本例中为Post)。我们有 他们的GPS坐标、他们要检查的建筑物/房间的UUID、他们的ID(通过身份验证)和类型 登记处、大楼或房间。比如:

    {
        "latitude": "33.333", // these only matter if it is 'building'
        "longitude": "-80.343", // these only matter if it is 'building'
        "buildingUuid": "ff97f741-dba2-415a-a9e0-5a64633e13ad", // could also be 'roomUuid' ...
        "type": "building", // or 'room'
        "exceptionReason": null
    }
    
    我有所有的能力来检查他们与建筑坐标的关系,查找建筑ID,等等 寻找如何使此代码简单、可维护和可测试的想法。首先,你可以看到我的 switch语句(far代码块)已经失控了,但我不知道如何构造它。这就是问题所在 问题的关键

    无任何要求:(feaux代码)

    我试图避免使用switch语句,但在某些情况下,我必须使用一些东西来确定要实例化什么, 正确的?多态性在这里对我没有帮助,因为我知道它对许多switch语句有帮助,因为我还不知道 有一个目标

    所以,对我来说,这似乎很简单。不清楚的是,我什么时候想添加一些需求。是什么 处理这种类型结构的正确方法(只涉及两种类型…这可能在短期内失控) 快点)

    (feaux代码)

    基于设计模式,如果我需要添加另一个
    $type
    ,我就在这里添加,这看起来相当不错。但是 这是一个控制器。。。它在滥用开关状态。。。因为所有的不同,它看起来很脆 实例化并传入的对象

    我有DIC,但我不知道它能让事情变得更清楚

    虽然它会清除这里的代码,但我不想在实际模型中实例化任何类(比如,我不想 在对象中创建'AlreadyTester'对象),因为我希望它是可测试的,这样做看起来像 将使测试/模拟变得更加困难

    话虽如此,测试也有同样的问题。在这些不同的需求和如何测试上有太多的依赖 他们认为,这些测试并不是非常孤立的。我可以模拟AlreadyTest和building/room对象来隔离 签入方法,并单独测试建筑/房间,但要同时测试它们,就像在集成测试中一样,现在我 冒不确定性测试的风险,因为我发现这是一种混乱的方法

    我最后的想法是这样的,但我担心控制器太胖了:(feaux代码)

    再一次,我觉得我应该把这两个if/throws(每个案例)抽象到其他地方,但这似乎并不能让我做到这一点 再简单一点(同时:我承认这不是一个复杂的例子……现在),控制器也不是更瘦 在这两个例子中。我觉得最后一个例子对我来说更清楚。对我来说,问题的关键是,每次都会发生一些事情 添加,这将意味着向switch语句添加更多内容。我认为多态系统会更好,但因为 需要外部类来进行需求检查,我必须实例化并传递大量的 对象,使其同样复杂。在每个签入对象内实例化类不是可测试的。我 我在想也许责任链或命令模式可能有用,但似乎不太合适

    我周而复始


    那么,其中一种方法是最好的,还是我可以做得更好?

    如果您开始将签入视为一个对象,您可以使用策略模式来表示每种不同类型的签入。然后你可以使用工厂为给定类型返回正确的签入对象;此外,为了在一个地方建造对象,我强烈考虑了工厂;但我现在所做的就是把switch语句移到那里(当然,它可能永远只需要在那个地方)。自从我写这篇文章以来,我一直在考虑的是DIC基于这个:这有助于我理解在一个实例中使用DI的情况是有多个级别的实例化。我使用粉刺,但使用的方式要简单得多(只返回一个“新”语句,而不是像工厂那样创建一个)。我的目标可能很高,但我以前曾与这种类型的结构作过斗争;想要传入一个用于检查某些内容的对象(如'alreadyCheck
    switch($type) {
        case 'building':
            $model = new \Namespace\Models\Building($this->container);
            break;
    
        case 'room':
            $model = new \Namespace\Models\Room($this->container);
            break;
    
        default:
            throw new \InvalidArgumentException("Type {$type} not understood");
    }
    
    $model->checkIn(); // polymorphic method that all 'type' classes will have, based on an interface implemented
    
    switch ($type) {
        case 'building':
            $model = new \Namespace\Models\CheckInBuilding($this->container);
            $alreadyInTest = new \Namespace\Models\Buildings\BuildingTestIn($this->container);
            $building = new \Namespace\Models\Buildings\Building($this->getData('buildingUuid'), $this->container);
    
            if (!$building) {
                throw new Exceptions\FailBadInput('Building not found: ' . $this->getData('buildingUuid'));
            }
    
            $model->setBuilding($building); // building object used for these properties: ID, coordinates for the lat/long 'nearby' check
            break;
    
        case 'room':
            $model = new \Namespace\Models\CheckInRoom($this->container);
            $alreadyInTest = new \Namespace\Models\Rooms\RoomTestIn($this->container);
            $room = new \Namespace\Models\Rooms\Room($this->getData('roomUuid'), $this->container);
    
            if (!$room) {
                throw new Exceptions\FailBadInput('Room not found: ' . $this->getData('roomUuid'));
            }
    
            $model->setRoom($room); // room object used for these properties: ID
            break;
    
        default:
            throw new \InvalidArgumentException("Type {$type} not understood");
    }
    
    $model->setAlreadyInTest($alreadyInTest);
    $model->setData($this->getData());
    $model->checkIn();
    //
    // Now, for 'building|room', this has all the data internal to check:
    //   - if they are nearby (but only if in building)
    //   - to test if they are already logged in
    //   - to log the primary key ID with the request
    //
    
    //
    // In addition, and not covered here, if they are not in range of the building, they can still check-in, but need to
    // type a short reason why they are doing a check-in and not in nearby range ('GPS broken, etc', who knows...). So,
    // would I add yet another class, instantiated here so it can be mocked in a test, and passed in empty, so it could
    // eventually be used to insert an exception reason (in a different table than the check-in table)?
    //
    
    switch($type) {
        case 'building':
            $alreadyInTest = new \Namespace\Models\Buildings\BuildingTestIn($this->container);
            if($alreadyInTest->isIn()) {
                throw new \InvalidArgumentException('You are already checked in to a building');
            }
    
            $building = new \Namespace\Models\Buildings\Building($this->getData('buildingUuid'), $this->container);
    
            if (!$building) {
                throw new Exceptions\FailBadInput('Building not found: ' . $this->getData('buildingUuid'));
            }
    
            $model = new \Namespace\Models\Building($this->container);
            break;
    
        case 'room':
            $alreadyInTest = new \Namespace\Models\Rooms\RoomTestIn($this->container);
    
            if($alreadyInTest->isIn()) {
                throw new \InvalidArgumentException('You are already checked in to a room');
            }
    
            $room = new \Namespace\Models\Rooms\Room($this->getData('roomUuid'), $this->container);
    
            if (!$room) {
                throw new Exceptions\FailBadInput('Room not found: ' . $this->getData('roomUuid'));
            }
    
            $model = new \Namespace\Models\Room($this->container);
            break;
    
        default:
            throw new \InvalidArgumentException("Type {$type} not understood");
    }
    
    $model->setData($this->getData());
    $model->checkIn();