Whip Monstrous Code Into Shape - 06 God Object Cleanup #3: Value Objects

file

Extracting value objects, when appropriate, can be a useful technique for cleaning up messy classes. Use a simple metric: if you find multiple pieces of behavior that surround a single primitive or value, consider a value object. Please note that developers often have a tendency to "value object all the things," so be careful. Refactor toward value objects, rather than adopting them by default for every possible value.


这个视频讲的是如何使用 Value Object. 从名称来看就可以了解, Value Object 就是把对象的某个属性抽象成一个类。什么情况下能用到呢?一个很简单的场景,比如有个 Performance 模型,这个模型有个 revenue (收入) 属性,我们在存储比如价格、费用的情况下都习惯用“分”来做单位,但是在前端显示的时候一般用 “元”,甚至还要加上货币名称。

通常的做法就是在模型里面加上不同的方法,比如把货币转换成元,加上货币名称,甚至转换成美金等等。

class Performance extends Model
{
    public function revenue()
    {
        return $this->revenue;
    }

    public function revenueInYuan()
    {
        return $this->revenue / 100;
    }

    public function revenueAsCurrency()
    {
        return money_format('¥%i', $this->revenueInYuan());
    }

    public function revenueInUsd()
    {
        return $this->revenueInYuan() / 6.9;
    }
}

当我们看到模型里面有关于某个属性很多不同的方法的时候,这个时候可能就要考虑把属性抽象成一个 Value Object,把这些不同的表现放到这个 Value Object 中。

class Revenue
{
    private $revenue;

    public function __construct($revenue)
    {
        $this->revenue = $revenue;
    }

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

    public function inYuan()
    {
        return $this->revenue / 100;
    }

    public function asCurrency()
    {
        return money_format('¥%i', $this->revenueInYuan());
    }

    public function revenueInUsd()
    {
        return $this->InYuan() / 6.9;
    }
}

我们已经创建了一个 Value Object,但是在用的时候每次都要实例化一个类,很麻烦的,有没有什么办法可以跟访问模型的属性一样?有,你可以在模型中添加一个 getter(访问器)

class Performance extends Model
{
    public function getRevenueAttribute($revenue)
    {
        return new Revenue($revenue);
    }
}

这个时候你通过 $performance->revenue 返回的就是一个 Revenue 实例了,你可以通过调用不同的方法获取不同的结果:$performance->revenue->asCurrency()就可以获取带有货币符号的值了。

更进一步的,你可以在 Revenue 中添加一个 __get 魔术方法,像访问 Revenue 属性一样获取结果。

public function __get($attribute)
{
    if (method_exists($this, $attribute)) {
        return $this->{$attribute}();
    }

    return $this->revenue;
}

你还可以给 Revenue 添加一个 __toString() 魔术方法,这样就可以在某些情况下把 Revenue 当作字符串访问了: echo $performance->revenue.

public function __toString()
{
    return (string) $this->revenue;
}

file

本帖已被设为精华帖!
本帖由 Summer 于 7年前 加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 12
Summer

加精后再看

7年前 评论

@Summer 大哥, Laravel-china 的速度真的很快啊

7年前 评论
Summer

@oustn 对了 OPCache 前阵关了,我去开开你等会再试试,应该能再快一倍

7年前 评论

@Summer 我其实是说你的速度很快,虽然 Laravel-china 也是飞快 :smile: hhha

7年前 评论
Summer

@oustn 你再试试

7年前 评论

@Summer 确实!今天我们项目组还在质疑 laravel 的性能,应该让他上咱们论坛看看这速度 杠杠的

7年前 评论
nickfan

InYuan大小写错了。。。~~~

7年前 评论

为啥。。。。这个系列的帖子没有打赏?

7年前 评论

@nickfan 好吧好吧,没注意,多谢提醒 懒得改了 看得懂就行 哈哈

7年前 评论

这个设计很赞!学习了!

7年前 评论
Lonexw

@wingofsky 应该需要作者在个人资料里面上传一下「微信收款二维码」 @oustn

7年前 评论

@JobsLong 我后来研究了一下,知道了要上传,不过这也不是什么高水平的文章 @wingofsky 打赏就不必了,能分享点东西给大家就好 :smile:

7年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!