我正致力于将一个大型的、单片的类分解为几个子类,但一次完成太多了,所以我希望在时间允许的情况下,将它们一个接一个地分解为几个版本。这是一个授权某些通道的身份验证类,所以目前它看起来是这样的:
$auth = new Auth($user, $data);
$output = $auth->authChannel($channelName);
在Auth
内部,它基本上看起来是这样的:
public function __construct($user, $data)
{
$this->user = $user;
$this->data = $data;
}
public function authChannel($channel)
{
$this->setUserData();
if (isset(self::CHANNEL_AUTH_FUNCTIONS[$channel])) {
$authFunction = self::CHANNEL_AUTH_FUNCTIONS[$channel];
return $this->$authFunction();
} else {
// invalid channel
}
}
所以self::CHANNEL_AUTH_FUNCTIONS
基本上就是['channelA' => 'authChannelA', 'channelB' => 'authChannelB']
,等等,所有这些函数都在这个类中。
现在我想做的是if $legacyChannel => callLegacyFunction() / else $newChannel => instantiate its own class and call auth()
,一次一个。
因此,我将Auth.php
放入它自己的名称空间,并在同一名称空间中拥有新的Channel.php
类。和CCD_ 7。
目前我有这个:
public function authChannel($channel)
{
$this->setUserData();
if (isset(self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel])) {
$authFunction = self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel];
if ($authFunction) {
return $this->$authFunction();
} else {
$authClassName = __NAMESPACE__ . '\' . ucwords($channel);
$authClass = new $authClassName($user, $data);
return $authClass->auth();
}
} else {
// invalid channel
}
}
有更好的方法吗?目前,这似乎有点浪费,因为创建了两个不同的对象,例如setUserData()
函数,我认为需要再次调用。我还想知道是否有比__NAMESPACE__ . / . $className
更好的方法来获得动态类名。
在代码看起来更好之前,您必须进行大量工作。我将尝试建议尽可能少的更改,以使"迁移"尽可能无痛,尽管您离干净的设计只有几步之遥。
首先,您可以为新的身份验证类创建一个AuthStrategyInterface
。
interface AuthStrategyInterface
{
public function supports(string $channel): bool;
public function auth($user, $data);
}
每个新的身份验证类都应该实现这个接口。方法supports($channel)
很容易理解:如果一个身份验证类可以处理某个通道,那么它应该返回true。
您的Auth
类需要一种方法来注入这些策略。通常你会在构造函数中这样做。。。但为了保持API不变,我们只创建一个setter方法。
当执行authChannel()
时,它将首先检查注入的策略,看看是否有任何策略支持使用的$channel
,并在可能的情况下使用它。如果没有,请返回检查旧的实现。
这样,在添加新的身份验证策略时,就不需要接触任何旧代码。当您添加新的实现时,您正在逐渐扼杀遗留系统。在某一点上,没有使用旧的实现,您可以进入新的代码重构阶段。
class Auth {
private iterable $strategies = [];
public function __construct($user, $data)
{
$this->user = $user;
$this->data = $data;
}
public function setAuthStrategies(iterable $strategies)
{
$this->strategies = $strategies;
}
public function authChannel($channel)
{
$this->setUserData();
// check if any of the new strategies supports
foreach ($this->strategies as $strategy) {
if ($strategy->supports($channel) {
return $strategy->auth($this->user, $this->data);
}
}
// check "legacy" authentication methods.
if (isset(self::CHANNEL_AUTH_FUNCTIONS[$channel])) {
$authFunction = self::CHANNEL_AUTH_FUNCTIONS[$channel];
return $this->$authFunction($this->user, $this->data);
}
// no valid authentication method
return false;
}
}
要使用它,你可以这样做:
$fooAuthStrategy = new FooAuthStrategy();
$barAuthStrategy = new BarAuthStrategy();
$bazAuthStrategy = new BazAuthStrategy();
$auth = new Auth($user, $data);
$auth->setAuthStrategies(
[
$fooAuthStrategy,
$barAuthStrategy,
bazAuthStrategy
]
);
$auth->authChannel($channel);
具体情况会根据你的应用程序的设置方式而变化,但这样的做法会比你目前的方法更进一步。
我不知道我是否正确理解了这个问题,但你不能这样做吗?
public function authChannel($channel)
{
$this->setUserData();
if (!isset(self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel])) {
// Invalid channel
return;
}
return self::CHANNEL_AUTH_LEGACY_FUNCTIONS[$channel]
? $this->$authFunction()
: parent::auth();
}