数据库

时间:2015-08-22 04:31:59

标签: php design-patterns configuration

如何在保持DRY的同时访问对象中的应用程序设置?

我不是指数据库凭据或API密钥之类的东西,它们保存在环境变量中。我在这里提到的设置是:

WORK_DAY_START_TIME     04:00:00
MAX_HOURS_PER_DAY       13
ALLOW_DAY_CROSSOVER     false

这些设置会影响业务逻辑,对应用程序是全局的,并且因组织而异。目标用户是可能不是技术人员的经理,因此有一个允许他们更改设置的用户界面。

值保存在数据库中(尽管持久性介质与此无关,因为我可以从XML / INI / JSON / YAML文件中轻松读取)。

这是一个访问设置的简单类(它比这更多一点但是为了这个问题的目的就够了):

<?php

class Settings implements \ArrayAccess
{
    private $settings = array();

    public function __construct() {
        // get settings from DB and put them in $this->settings
    }

    public function offsetGet($key) {
        return $this->settings[$key];
    }
}

// In the entry point file:
$settings = new Settings();

检查时我需要值:

class Shift extends EntityObject
{
    private function isValid() {
        // For illustrative purposes only
        return !$settings['ALLOW_DAY_CROSSOVER'] && $this->start < $settings['WORK_DAY_START_TIME'] && $this->end < $settings['WORK_DAY_START_TIME'];
    }
}

以下是我提出的一些选项:

  1. global $settings或其替代$GLOBALS['settings']。这依赖于一个全局变量,所以不好。
  2. Settings成为单身人士。几乎同样的问题,用单元测试很难。
  3. 在每个类的构造函数中,实例化Settings以获取设置。这似乎是很多开销,特别是当设置已经加载时。
  4. Settings的实例传递给需要设置的每个对象的构造函数(基本上所有对象)。这听起来像依赖注入,如果我理解正确但似乎重复?
  5. 我该如何解决这个问题?

    如果我使用Zend_Config,我想我会遇到同样的问题。

    相关问题是$settings array or Config Class to store project settings?

2 个答案:

答案 0 :(得分:2)

  1. 使用全局。出于多种原因,这是个坏主意。首先,它打破了封装,这是面向对象编程的全部要点。它也隐藏了一个依赖..事实是你的类需要设置才能执行它的工作,但如果不阅读所有代码,这个事实就不明显了。单元测试也变得更加困难,因为在运行每个测试之前必须考虑全局状态。另请参阅spooky action at a distance

  2. 将设置设为单身人士。这基本上遇到了全局变量所具有的所有相同问题......它只是用对象表示法包装。关于你为什么不应该使用单身人士的一个很好的观察表现在Clean Code Talk

  3. 为每个方法调用实例化一个新的设置对象可能是一个很好的解决方案,因为您有这些设置文件的许多不同变体。

  4. 将依赖项注入构造函数是一种很好的做法。这使得您的类需要一个设置对象来执行它的工作。这个选项虽然有些考虑,但在构建过程中经常为你的类提供合作者可能会很棘手。使用依赖注入容器可以使这更简单。

  5. 另一件需要注意的事情是,你的名字设置并不是非常具有描述性,并没有真正传达这个对象的责任。您可能希望使用更具描述性的类名来准确传达对象的内容。

    <?php
    
    class EmployeeShiftPolicy {
        private $max_hours_per_shift;
        private $allow_day_crossover;
        private $workday_start_time;
        private $workday_end_time;
    
        public function __construct(
                                    $max_hours,
                                    $allow_crossover,
                                    \DateTimeInterface $workday_start,
                                    \DateTimeInterface $workday_end){
            $this->max_hours_per_shift = $max_hours;
            $this->allow_day_crossover = $allow_crossover;
            $this->workday_start_time  = $workday_start;
            $this->workday_end_time  = $workday_end;
    
        }
    
        public function getMaxHoursPerShift(){
            return $this->max_hours_per_shift;
        }
    
        // other accessors
    
        public function validateShiftProposal(\DateTimeInterface $startTime, \DateTimeInterface $endTime){
            ...
        }
    }
    

    然后,当您构建班次时,您可以为其提供适当的政策。即一个用于周末,一个用于学龄儿童,一个用于有工作限制的人......等等。

    希望这将为您如何为您的域建模提供一些指导。

答案 1 :(得分:0)

非常有趣,你的方法。

  1. 同意,不要使用$ GLOBALS
  2. 同意,singelton不是OOP
  3. 同意,难以维护
  4. 很好的方法,DI对于不同的实现很方便。您现在有一个实现,但DI使您的应用程序更具可扩展性/可扩展性。这个选项非常OOP!
  5. (会话是另一种选择。容易进行单元测试并存储在内存中(如果你有足够的话),例如memcached或redis,它的速度非常快。)