将功能封装到类中

时间:2015-02-14 00:38:05

标签: php oop

我有一个相当大的类XYZ,它具有不同的行为,给出了不同的参数。每个论点都应有一个帮助文本。

由于类XYZ非常大,我想将所有参数函数封装到argumentManager类中,并将所有帮助相关的函数封装到helpManager类中。这是可能的,因为它们具有独立于XYZ的功能。结果是这样的:

<?php

class ArgumentManager {

    // DO NOT CALL
    public function addArgument($option, $argumentCount) {
        ...
    }

    // Other functions MAY be called by the user
    public function isArgumentInList($option) {
        ...
    }

    ...

}

class HelpManager {

    // DO NOT CALL
    public function addHelpEntry($option, $helptext) {
        ...
    }

    // Other functions MAY be called by the user
    public function printHelpPage() {
        ...
    }

    ...

}

class XYZ {

    private $helpManager;
    private $argumentManager;

    // The user may call everything in there, EXCEPT HelpManager::addHelpEntry, since it must be a created pair by XYZ::addArgumentWithHelp
    public function getHelpManager() {
        return $this->helpManager;
    }

    // The user may call everything in there, EXCEPT ArgumentManager::addArgument, since it must be a created pair by XYZ::addArgumentWithHelp
    public function getArgumentManager() {
        return $this->argumentManager;
    }

    // Create classes which contain independent functionalities
    public function __construct() {
        $this->helpManager = new HelpManager();
        $this->argumentManager = new ArgumentManager();
    }

    // Intended usage for creating an argument with help (they should always be a couple)
    public function addArgumentWithHelp($option, $helptext, $argumentCount) {
        $this->argumentManager->addArgument($option, $argumentCount);
        $this->helpManager->addHelpEntry($option, $helptext);
    }

    // Many other functions of the big class XYZ
    .....

}

班级XYZ现在要小得多。

可以通过调用$XYZ->addArgumentWithHelp()添加带有帮助文本的参数。

可以调用与帮助相关的功能相关的功能,例如通过$XYZ->getHelpManager()->printHelpPage()。与参数相关的函数也是如此。

问题在于我不希望$XYZ->getHelpManager()->addHelpEntry()$XYZ->getArgumentManager->addArgument()XYZ以外的任何人调用,因为我想强制执行argumentManagerhelpManager {{1}}有关于该选项的信息。

6 个答案:

答案 0 :(得分:1)

在您提出的初步问题中,您问道:

  

由于XYZ类非常大,我想将所有参数函数封装到一个argumentManager类中,并将所有与帮助相关的函数封装到一个helpManager类中。

所以看起来你真正需要实现的只是使用traits而不需要任何额外的类。这是我如何构建它,而不包括所有代码(您可以从原始问题中剪切和粘贴):

<?php

trait ArgumentManagerTrait {

    // DO NOT CALL
    protected function addArgument($option, $argumentCount) {
        ...
    }

    // Other functions MAY be called by the user
    public function isArgumentInList($option) {
        ...
    }

    ...

}

trait HelpManagerTrait {

    // DO NOT CALL
    protected function addHelpEntry($option, $helptext) {
        ...
    }

    // Other functions MAY be called by the user
    public function printHelpPage() {
        ...
    }

    ...

}

class XYZ {

    use HelpManagerTrait, ArgumentManagerTrait;

    // Intended usage for creating an argument with help (they should always be a couple)
    public function addArgumentWithHelp($option, $helptext, $argumentCount) {
        $this->addArgument($option, $argumentCount);
        $this->addHelpEntry($option, $helptext);
    }

    // Many other functions of the big class XYZ
    .....

}

在此解决方案中,您不需要任何其他类或内部对象,只需使用特征封装所有内容。它更清洁,更整洁,这是我选择的解决方案。因为HelpManagerTrait和ArgumentManagerTrait是父类的特征,所以它们的方法(受保护和公共)成为父类的一部分。

答案 1 :(得分:0)

因此,您需要制作属性privateprotected,以便只能通过XYZ访问它们:

class XYZ 
{
    protected $helpManager;
    protected $argumentManager;

    public function __construct(ArgumentManager $argumentManager, HelpManager $helpManager) {
         $this->helpManager = $helpManager;
         $this->argumentManager = $argumentManager
    }

    // CORRECT USAGE
    public function addArgument($option, $helptext) {
        $this->argumentManager->addArgument($option, $argumentCount);
        $this->helpManager->addHelpEntry($option, $helptext);

        return $this;

    }

我觉得你的设计在这里可能会更好但除此之外我不能说因为它不清楚这三个类如何真正互动。如果我有更多信息,id可能还有其他建议/解决方案。

答案 2 :(得分:0)

听起来像你在谈论C ++风格的友谊,其中某些类具有对所谓朋友类的私有和受保护成员的特权访问权限。不幸的是PHP中没有实现友谊。如果您正在寻找使用命名空间来改变可见性的复杂解决方法,您可以阅读以下文章:Nested Or Inner Classes in PHP

答案 3 :(得分:0)

您可以使用特征完成所需。

将参数管理器和帮助管理器的主要部分声明为特征,将您不希望从外部调用的函数视为受保护。

声明真正的参数管理器和帮助管理器类使用这些特征。

在XYZ内部声明了真实参数管理器和帮助管理器类的变体,但是将受保护函数的可见性覆盖为public。然后从XYZ内部,您可以将类型转换从真实类输入到变体类,并调用您需要在内部副本上调用的方法。

很抱歉缺少代码示例,我在平板电脑上输入了这个。如果你想要一些我可以在以后提供它们,但同时阅读有关特征的php手册部分。

答案 4 :(得分:0)

我想我找到了一个解决方案/解决方法。使用反射,我强制通过在运行时更改可见性来调用addArgument,这只会在类XYZ上发生。用户不能无意中访问这些受保护的方法。

<?php

class ArgumentManager {

    // Will be called by "friend" class XYZ
    protected function addArgument($option, $argumentCount) {
        ...
    }

    // Other functions MAY be called by the user
    public function isArgumentInList($option) {
        ...
    }

    ...

}

class HelpManager {

    // Will be called by "friend" class XYZ
    protected function addHelpEntry($option, $helptext) {
        ...
    }

    // Other functions MAY be called by the user
    public function printHelpPage() {
        ...
    }

    ...

}

class XYZ {

    private $helpManager;
    private $argumentManager;

    public function getHelpManager() {
        return $this->helpManager;
    }

    public function getArgumentManager() {
        return $this->argumentManager;
    }

    // Create classes which contain independent functionalities
    public function __construct() {
        $this->helpManager = new HelpManager();
        $this->argumentManager = new ArgumentManager();
    }

    // Intended usage for creating an argument with help (they should always be a couple)
    public function addArgumentWithHelp($option, $helptext, $argumentCount) {
        $argMethod = new ReflectionMethod($this->argumentManager, 'addArgument');
        $argMethod->setAccessible(true);
        $argMethod->invoke($this->argumentManager, $option, $argumentCount);

        $helpMethod = new ReflectionMethod($this->helpManager, 'addHelpEntry');
        $helpMethod->setAccessible(true);
        $helpMethod->invoke($this->helpManager, $option, $helptext);
    }

    // Many other functions of the big class XYZ
    .....

}

答案 5 :(得分:0)

这是另一个答案,我必须有点哲学。你可以声明你的类和特征:

<?php

trait ArgumentManagerTrait {

    // DO NOT CALL
    protected function addArgument($option, $argumentCount) {
        ...
    }

    // Other functions MAY be called by the user
    public function isArgumentInList($option) {
        ...
    }

    ...

}

trait HelpManagerTrait {

    // DO NOT CALL
    protected function addHelpEntry($option, $helptext) {
        ...
    }

    // Other functions MAY be called by the user
    public function printHelpPage() {
        ...
    }

    ...

}

class HelpManager { use HelpManagerTrait; }
class ArgumentManager { use ArgumentManagerTrait; }
class HelpManagerInternal { use HelpManagerTrait { addHelpEntry as public; }
class ArgumentManagerInternal { use ArgumentManagerTrait { addArgument as public; }

class XYZ {

    protected $helpManager; // of class HelpManagerInternal
    protected $argumentManager; // of class ArgumentManagerInternal

    public function __construct(ArgumentManager $argumentManager, HelpManager $helpManager) {
         $this->helpManager = $this->helpManagerCastToInternal($helpManager);
         $this->argumentManager = $this->argumentManagerCastToInternal($argumentManager);
    }

    // Intended usage for creating an argument with help (they should always be a couple)
    public function addArgumentWithHelp($option, $helptext, $argumentCount) {
        $this->argumentManager->addArgument($option, $argumentCount);
        $this->helpManager->addHelpEntry($option, $helptext);
    }

    public function getHelpManager() {
        return $this->helpManagerCastToExternal($this->helpManager);
    }

    public function getArgumentManager() {
        return $this->argumentManagerCastToExternal($this->argumentManager);
    }

    // Many other functions of the big class XYZ
    .....

}

你会注意到我在这里省略了4个函数 - helpManagerCastToInternal,helpManagerCastToExternal和argumentManager相同的2。如何实现这些取决于你在问题中没有给出的argumentManager和helpManager traits / classes的内部属性结构。

我要做的是让两个类都来自Laravel \ Fluent或Symfony \ Component \ HttpFoundation \ ParameterBag之类的东西。这些是框架中的类,您可以使用composer引入这些类,并为您提供一些基本功能,例如以setter / getter等逻辑方式内部存储属性。然后,您的强制转换函数可以只获取一个对象的所有属性,创建另一种类型的新对象,然后将属性从第一个对象推送到第二个对象 - castToInternal和castToExternal基本上执行彼此的工作,但相反。请注意,这实际上是一个对象 copy ,而不是一个对象强制转换,所以如果你的helpManager和argumentManager中的其他函数混淆了内部属性,你需要在某些时候将它们复制回去,这可能不是你想要的。

或者您可以使用此处建议的反射方法:How to Cast Objects in PHP - 无论您的内部属性结构如何,都可以使用。

这实际上取决于你想要如何构造和使用你的类的内部属性(因此有点哲学)。