我想为Session
和Request
创建一个包装器,这样我就不必直接访问PHP超级全局了。我意识到如果我为超级全局创建一个包装器并使用它们,单元测试我的应用程序会更容易,因为包装类可以被模拟。
在尝试创建我的包装类时,我研究了一些示例包装类。其中一些在初始化时将超全局存储为类属性:
class Session
{
protected $vars;
public function __construct()
{
session_start();
// POINT OF INTEREST
// Store the superglobal as a class property
$this->vars = $_SESSION;
}
public function get($index)
{
// POINT OF INTEREST
// Accesses the class property instead of the superglobal
return $this->vars[$index];
}
public function write($index, $value)
{
// Writes both class property and session variable
$this->vars[$index] = $value;
$_SESSION[$index] = $value;
}
}
我的问题:有没有什么特别的原因为什么在创建包装类时我们将超全局存储为类'属性而不是直接访问它们?将上述代码与此对比:
class Session
{
public function __construct()
{
session_start();
}
public function get($index)
{
// Accesses the superglobal directly
return $_SESSION[$index];
}
public function write($index, $value)
{
// Accesses the superglobal directly
$_SESSION[$index] = $value;
}
}
IMO,因为包装类无论如何都会被嘲笑,为什么还要将超级全局存储为类属性呢?有这么多人这样做的原因有什么特别的吗?我应该将超级全局存储为包装器中的属性而不是直接访问它吗?
感谢您的任何意见。
答案 0 :(得分:5)
会话是一个非常具体的案例。但是你问是否有任何理由来包装超级全局变量。以下是一些可能的原因(不按顺序,也不完整):
使代码更少依赖于全局状态,因此更容易测试。您可以测试依赖于全局状态的代码。但它比测试被告知其状态的代码更难,更脆弱。
为了使代码更加灵活,因为您可以伪造请求和子请求来执行真正的全局状态无法实现的有趣事情。
使代码更具可移植性。通过将其包装在包装器中,您可以在中央位置处理与平台相关的事物,例如剥离引号,处理字符集转换等。这可以更容易地处理平台或多个平台之间的转换。
对变量强制执行其他约束。由于$ _SESSION允许您在自身内部设置任何内容,因此可以使用可能导致问题的非可序列化状态。使用包装器,您可以使用一个集中点来检查状态,以确定它是否符合必要的约束条件。
使您的代码更具可读性。当然,几乎每个php开发人员都知道如果你在一个方法中访问$ _POST你正在做什么。但他们是否需要了解该实施细节?或$request->getFromPostData('foo');
更详细?
为了使代码更易于调试,因为您可以在请求类中设置断点并立即查找所有访问请求变量(只要您永远不会直接访问它们)。
使您的类的依赖项更容易理解。如果我为使用超级全局变量的类提供了API,则无法判断该类是否访问过它,因此无论该类是否需要操作。但是如果你需要注入一个请求类,你可以快速地看出该类实际上确实需要来自请求操作的东西。因此,提高可读性并更多地阐明您的API。
现实中有更多的理由,但那些是我能想到的最重要的原因。
答案 1 :(得分:3)
我认为没有一些重要的原因,也许只是如果你改变了超级全局,你之后指的是 ,你就没有必要更换它在你的类的整个代码中,但只是在构造函数中。
从这个角度来看,我发现你提供的第一个实现的write
方法并不是很明智,它会像你所说的那样写出类属性和会话变量。