27

我想做一些类似于Objective-C中的类类别的事情,在一个文件中定义一个类,在那里实现一些核心方法,然后在另一个没有子类化或接口的情况下实现一些帮助方法,只是“继续”这个类。

在PHP中可能吗?

4

7 回答 7

43

从 PHP 5.4 开始,您可以使用Traits完成此操作。引用官方文档:

Traits [使] 开发人员可以在生活在不同类层次结构中的几个独立类中自由地重用方法集。[...] Trait 类似于类,但仅旨在以细粒度和一致的方式对功能进行分组。无法单独实例化 Trait。它是对传统继承的补充,可以实现行为的横向组合;即类成员的应用不需要继承。

使用 trait 允许您将解决横切关注点的辅助方法存储在一个中心位置,并在您需要的任何类中使用这些方法。

// File "HelperMethodTrait.php"
trait HelperMethodTrait
{
    public function myIncredibleUsefulHelperFunction()
    {
        return 42;
    }
}

// File "MyClass.php"
class MyClass
{
    use HelperMethodTrait;

    public function myDomainSpecificFunction()
    {
        return "foobar";
    }
}

$instance = new MyClass();
$instance->myIncredibleUsefulHelperFunction(); // 42
于 2013-05-16T21:34:23.227 回答
18

a-priori PHP 没有通过语言构造给你这个特性,整个类必须在一个文件中。

现在有一些技巧可以做到这一点,它们对 CPU 的要求非常高:

例如,您可以在不同文件之间分离类,然后加载文件并使用整个代码构建单个字符串,然后执行 eval(string) 来解释类并将它们构建到可运行代码区域中。

顺便说一句,这不是一个好主意,原因有很多

于 2013-05-16T21:08:31.863 回答
6

如果您的唯一目标是使类定义文件更小,则可以在函数定义中包含文件

class MyClass {
    public function doStuff($foo,$bar) {
        global $quz;
        include('MyClass.doStuff.php');
    }
}

在包含中,您的本地范围适用(将定义 $foo、$bar 和 $quz)

我不确定开销是多少 - 包含似乎发生在运行时,而不是编译时(因为包含实际上可以返回一个值..)

于 2013-12-27T23:46:13.280 回答
3

我认为你最好按照功能标准将你的类分成 n 个部分,然后使用依赖注入模式将类 A“注入”到 B 的构造函数中的类 B 中,而不需要子类化/接口。

于 2014-05-26T17:09:35.650 回答
1

您可以使用此方法在外部向某些对象添加函数,这与向类本身添加方法不同。

https://stackoverflow.com/a/2938020/2277620

于 2013-05-16T21:29:54.647 回答
1

另一个不太好的方法是它创建了多个子类,因此每个级别的子类只添加一个“组”函数。

我在一个类超过 10k 行的单个案例中使用了这个技巧。

现在我有 6 个继承级别,其中每个级别都添加一个“上一级”类的函数“类别”。

不那么消耗cpu,难看,不那么难维护

于 2015-05-29T07:51:37.217 回答
0

这在很大程度上取决于您希望实现的目标。我有这个好主意,做你想做的事。我喜欢关键词集,运行删除等。假设有一个类,我想要删除集。正如Ph.T指出的那样,课程需要在单个文件中

所以在一个文件中设置 abc->set 并在另一个文件中设置 abc->get 没有任何用处和意义。

因此不得不进行大量的重新思考,并将类视为执行功能分解的特定任务的微服务。

解决方案与名称间距一起使用。

        namespace A;
    class Get{public static function myCode(){}
public static function myAnotherCode(){}}

这将被称为 A\Get::myCode(); 或 A\Get::myAnotherCode(); 同样设置将是

        namespace A;
    class Set{public static function myCode(){}
public static function myAnotherCode(){}}

这将被称为 A\Set::myCode(); 或 A\Set::myAnotherCode();

然后用户 spl_autoload_register 加载类记得用 / 替换 \

spl_autoload_register(function ($class) {
 require_once __DIR__ . '/' . strtolower(str_replace('\\', '/', $class) . '.php')
});

目录结构如下

main (folder)
--  A (folder)
    --  set.php (file)
    --  get.php (file)

我们要求 A\Get::myCode() 要求将其视为 A/Get.php 然后运行其逻辑,无论它是正确的名称空间还是类等。

如果计划得当,它的好处是它会迫使你以合乎逻辑的方式运行事情,而不仅仅是在那里和那里虚无缥缈。其次,您只会加载您需要的功能,而不仅仅是一大堆这个和那个。

不利的一面是,类的两个部分不会在它们之间共享,除非一个从另一个扩展一个,这将违背整个逻辑,因为另一个会因此以任何方式加载。

必须仔细否定将共享哪些逻辑和计划数据,并且必须事先制定好。

namespace A;
class Universal {
protected static $share_one;
protected static $share_two;
}

namespace A;
class Set extends Universal {
public static function myCode(){ parent::$share_one ='wow';}
}

namespace A;
class Get extends Universal {
public static function myCode(){ return parent::$share_one;}
    }

    main (folder)
    --  A (folder)
        --  universal.php (file)
        --  set.php (file)
        --  get.php (file)

运行代码,看看魔法

 A\Set::myCode();
 echo  A\Get::myCode();  

如果您只需要一组功能,例如 get,那么只需使用 get echo A\Get::myCode();就可以正常工作。

老实说,如果不玩命名空间,它只是花费时间而令人头疼。旁注:如果做得好,它可以加快速度,因为通用类不会加载两次。即使调用 gazillion 倍我们想要加载默认值,然后在我们需要它们的地方填充它们。我制作了巨大的类,除了默认值之外,只需要很少的函数,其余的只是内存加载。分解只是加速事情。

于 2021-02-24T21:03:23.257 回答