From Apprentice To Artisan 笔记(一)
1.依赖注入
关注分离
每个类应该有独立的职责,并且该职责完全被这个类封装。好处就是 Web 控制器和数据访问解耦。这会使得实现存储迁移更容易,测试也会更容易。“Web” 就仅仅是为你真正的应用做数据的传输了。
如何做
定义一个接口,并实现该接口
//1.定义接口
interface BillerInterface {
public function bill()
}
interface BillingNotifierInterface {
public function notify()
}
//2.实现接口
class BService implements A {
public function notice(){
...
}
}
//依赖注入
class AController implements BillerInterface
{
public function _contrust(BillingNotifierInterface $notifier)
{
$this->service = $notifier;
}
public function bill()
{
$this->service->notify();
}
}
}
类只要遵守了约定,就不用再考虑如何实现提示功能。只要是遵守约定(接口)的类, 类都能用。这不仅仅是方便了我们的开发,而且我们还可以通过模拟 Interface 来进行无痛测试。
写接口可能看上去挺麻烦,但实际上能加速你的开发。你不用实现任何接口,就能使用模拟库来模拟你的接口,进而测试整个后台逻辑!
对于小而简单的应用来说,接口通常是不必要的。在你确信不会改变的地方就没有必要使用接口了。
在大型应用中接口是很有帮助的。和提升的代码灵活性、可测试性比起来,多敲键盘费的功夫就微不足道了。
如果你不喜欢写接口,那就先简单的写代码吧。日后再精进即可。
2.控制反转容器 IOC
IoC 容器可以使你更容易管理依赖注入,Laravel 框架拥有一个很强大的IoC容器。Laravel 的核心就是这个 IoC 容器,这个 IoC 容器使得框架各个组件能很好的在一起工作。事实上 Laravel 的 Application 类就是继承自 Container 类!
如何在应用中管理、注入这些对象?
- 可以将 Stripe 的支付实现绑定到容器里
App::bind('BillerInterface', function() { return new StripeBiller(App::make('BillingNotifierInterface')); });
- 处理 BillingInterface 时,我们额外需要一个 BillingNotifierInterface 的实现,也就是再来一个 bind:
现在不管在应用的哪里需要一个提示器,我们总会得到SmsBillingNotifier的对象。App::bind('BillingNotifierInterface', function() { return new EmailBillingNotifier; });
可以通过改变一行代表就可以改变返回的对象(短信运营商)
在应用中只实例化某类一次?使用 singleton 方法
容器的 instance 方法和 singleton 方法很类似,区别是 instance 可以绑定一个已经存在的对象。然后容器每次返回的都是这个对象了。
反射解决方案
用反射来自动处理依赖是 Laravel 容器的一个最强大的特性。反射是一种运行时探测类和方法的能力。比如,PHP 的 ReflectionClass 可以探测一个类的方法。method_exists 某种意义上说也是一种反射。
class UserController extends BaseController
{
public function __construct(StripBiller $biller)
{
$this->biller = $biller;
}
}
注意这个控制器的构造函数暗示着有一个 StripBiller 类型的参数。使用反射就可以检测到这种类型暗示。当 Laravel 的容器无法解决一个类型的明显绑定时,容器会试着使用反射来解决。程序流程类似于这样的:
已经有一个 StripBiller 的绑定了么?
没绑定?那用反射来探测一下 StripBiller 吧。看看他都需要什么依赖。
解决 StripBiller 需要的所有依赖(递归处理)
使用 ReflectionClass->newInstanceArgs() 来实例化 StripBiller
假设我们没有为 BillerInterface 做任何绑定, 容器该怎么知道要注入什么类呢?要知道 interface 不能被实例化,因为它只是个约定。如果我们不提供更多信息的话,容器是无法实例化这个依赖的。我们需要明确指出哪个类要实现这个接口,这就需要用到 bind 方法:
App::bind('BillerInterface','StripBiller');
这里我们只传了一个字符串进去,而不是一个匿名函数。 这个字符串告诉容器总是使用 StripBiller 来作为 BillerInterface 的实现类。 此外我们也获得了只改一行代码即可轻松改变实现的能力。比如,假设我们需要切换到 Balanced Payments 作为我们的支付提供商,我们只需要新写一个 BalancedBiller 来实现 BillerInterface 接口,然后这样修改容器代码:
App::bind('BillerInterface', 'BalancedBiller');
可以使用 singleton 方法来实现单例模式
App::singleton('BillerInterface', 'StripBiller');
扩展 单例模式
在PHP中,所有的变量无论是全局变量还是类的静态成员,都是页面级的,每次页面被执行时,都会重新建立新的对象,都会在页面执行完毕后被清空,这样似乎PHP单例模式就没有什么意义了,所以PHP单例模式我觉得只是针对单次页面级请求时出现多个应用场景并需要共享同一对象资源时是非常有意义的。
一个应用中会存在大量的数据库操作,比如过数据库句柄来连接数据库这一行为,使用单例模式可以避免大量的new操作,因为每一次new操作都会消耗内存资源和系统资源。 如果系统中需要有一个类来全局控制某些配置信息,那么使用单例模式可以很方便的实现.
随便加上 _clone 深浅复制支持
PHP 是一种鸭子类型的语言。所谓鸭子类型的语言,一个对象可用的方法取决于使用方式,而非这个方法从哪儿继承或实现。 换言之在程序里,一个对象看上去是个 User ,方法响应也像个 User ,那他就是个 User 。
接口就是约定。接口不包含任何代码实现,只是定义了一个对象应该实现的一系列方法。如果一个对象实现了一个接口,那么我们就能确信这个接口所定义的一系列方法都能在这个对象上使用。因为有约定保证了特定方法的实现标准,通过多态也能使类型安全的语言变得更灵活。
多态含义很广,其本质上是说一个实体拥有多种形式。在本书中,我们讲多态是一个接口有着多种实现。比如 UserRepositoryInterface 可以有 MySQL 和 Redis 两种实现,每一种实现都是 UserRepositoryInterface 的一个实例。
php 面对对象三大特性: 封装、继承、多态
- 抽象
抽象类是一个编程概念,Abstract Classes。在设计模式中,抽象类不能够被实例化/初始化,但是可以依靠具体类的继承(extends )来实现。- 接口
PHP接口类interface就是一个类的领导者,指明方向,子类必须完成它指定方法。- 接口 vs 抽象
- 接口使用implements,抽象用extends
- 接口没有成员变量,抽象有,可实现数据的封装
- 接口没有构造函数,抽象有
- 接口都是public,抽象类有private、protected、public
- 一个类可同时实现多个接口,但值能实现一个抽象类
- 封装
- 继承
abstract class Animal {} class Whale extends Animal {}
- 多态
多态是指在面向对象中能够根据使用类的上下文来重新定义或改变类的性质和行为。
唯独这个多态,php体现的十分模糊。原因是php是弱类型语言
上次面试的时候把我问懵了
接口就像是大纲,在开发程序的“骨架”时非常有用。在设计组件时,使用接口进行设计和讨论都是对你的团队有益处的。比如定义一个 BillingNotifierInterface 然后讨论他有什么方法。在写任何实现代码前先用接口讨论好一套好的 API!
本作品采用《CC 协议》,转载必须注明作者和本文链接