子类型和基类型转换_子类型多态-在运行时交换实现

子类型和基类型转换

How many people doubt the relevance of Inheritance and Polymorphism in Object-Oriented design? Probably very few, most likely because of ignorance or a narrow mindset. But there is one small gotcha here that can’t be easily overlooked. While understanding the logic that pulls on the reins of Inheritance is straightforward, things are more difficult when it comes to digging deep into the nuts and bolts of Polymorphism.

有多少人怀疑继承和多态性在面向对象设计中的相关性? 可能很少,很可能是由于无知或心态狭窄所致。 但是这里有一个不容忽视的小陷阱。 尽管理解继承的逻辑很简单,但是要深入研究多态性的细节,事情就更加困难了。

The term Polymorphism is intimidating in its own right, and its academic definition is plagued with several disparate slants which make it even harder to understand what’s actually under its hood. Peripheral concepts such as Parametric Polymorphism and ad-hoc Polymorphism, usually implemented through method overriding/overloading, do have prolific niches in some programming languages, but the last instance should be dropped when it comes to designing extensible systems capable of consuming specific contracts (read abstractions) without having to check if the implementers belong to an expected type.

多态性一词本身就令人生畏,它的学术定义受到数种不同倾向的困扰,这使得人们更难以理解其内幕。 通常通过方法重写/重载实现的外围概念(例如参数多态性和即席多态性)在某些编程语言中确实有很多优势,但是在设计能够使用特定合同的可扩展系统时,应该放弃最后一个实例(阅读抽象),而无需检查实现者是否属于期望的类型。

In short, most of the time any generic reference made to Polymorphism in OOP is implicitly assumed as the ability exposed by a system for defining a set of contracts or interfaces which in turn are honored by different implementations. This kind of “canonical” Polymorphism is commonly known as subtype Polymorphism, as the implementers of the interfaces are considered subtypes of them regardless if there’s an actual hierarchy down the line or not.

简而言之,大多数情况下,对OOP中对多态性的任何一般性引用都被隐式假定为系统定义一组契约或接口的能力,而契约或接口又被不同的实现方式所尊重。 这种“规范”多态性通常称为子类型多态性,因为接口的实现者被认为是它们的子类型,而不管是否存在实际的层次结构。

As one might expect, understanding the nature of Polymorphism is only half of the learning process; the other half is, of course, demonstrating how to design polymorphic systems that can be accommodated to work in pretty realistic situations, without getting caught in the trap of showcasing just “some nifty didactical code” (in many cases a cheap euphemism for toy code).

如人们所料,了解多态性的本质只是学习过程的一半。 另一半当然是演示如何设计可以在非常现实的情况下工作的多态系统,而不会陷入展示“一些漂亮的教学代码”的陷阱(在许多情况下,廉价的委婉说法是玩具代码) )。

In this article I’ll show you how to exploit the virtues that Polymorphism offers through the development of a pluggable cache component. The core functionality can be expanded later to suit your needs through the development of additional cache drivers.

在本文中,我将向您展示如何通过开发可插拔缓存组件来利用Polymorphism提供的优点。 以后可以通过开发其他缓存驱动程序来扩展核心功能,以满足您的需求。

定义组件的接口和实现 (Defining the Component’s Interfaces and Implementations)

The menu of available options to choose from when building an extensible caching component is anything but scarce (if you’re skeptical, just peek behind the curtain of some popular frameworks). In this case in particular, though, the component I present here has the nifty ability to swap out different cache drivers at runtime, all without having to amend a single chunk of client code.

构建可扩展的缓存组件时,可供选择的菜单几乎是稀缺的(如果您对此表示怀疑,请在一些流行框架的幕后窥视一下)。 不过,在这种情况下,尤其是在这种情况下,我在这里介绍的组件具有在运行时交换出不同的缓存驱动程序的精妙功能,而所有这些都无需修改客户端代码的单个块。

So how do we get this going without sweating too much during the development process? Well, the first logical step to take would be …yep, define a segregated cache contract which will be agreed to later by distinct implementations, hence taking advantage of the benefits of Polymorphism.

那么,在开发过程中如何在不费劲的情况下实现这一目标呢? 好吧,首先要采取的逻辑步骤是……是的,定义一个隔离的缓存协定,稍后将通过不同的实现方式对此协定,从而利用多态的好处。

At its most basic level, the aforementioned contract looks like this:

在最基本的层面上,上述合同如下所示:

<?php
namespace LibraryCache;

interface CacheInterface
{
    public function set($id, $data);
    public function get($id);
    public function delete($id);
    public function exists($id);
}

The CacheInterface interface is a skeletal contract that abstracts the behavior of a generic caching element. With the interface in place, it’s easy to create a few concrete cache implementations that adheres to the its contract.

CacheInterface接口是抽象通用缓存元素行为的骨架协定。 有了适当的接口,就可以轻松创建一些遵守其合同的具体缓存实现。

Since I want to keep things concise and easy to assimilate, the cache drivers I set up will be just a skinny duet: the first one uses the file system as the underlying backend for caching/fetching data, while the second one employs the APC extension behind the scenes.

由于我想使事情简洁明了,易于理解,因此我设置的缓存驱动程序将是一个皮包骨头的二重奏:第一个使用文件系统作为底层后台来缓存/获取数据,而第二个使用APC扩展在幕后。

Here’s the file-based cache implementation:

这是基于文件的缓存实现:

<?php
namespace LibraryCache;

class FileCache implements CacheInterface
{
    const DEFAULT_CACHE_DIRECTORY = 'Cache/';
    private $cacheDir;

    public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) {
        $this->setCacheDir($cacheDir);
    }
    
    public function setCacheDir($cacheDir) {
        if (!is_dir($cacheDir)) {
            if (!mkdir($cacheDir, 0644)) {
                throw InvalidArgumentException('The cache directory is invalid.');
            }
        }
        $this->cacheDir = $cacheDir;
        return $this;
    }
    
    public function set($id, $data) {
        if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) {
            throw new RuntimeException("Unable to cache the data with ID '$id'.");
        }
        return $this;
    }
    
    public function get($id) {
        if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) {
            throw new RuntimeException("Unable to get the data with ID '$id'.");
        }
        return $data;
    }
    
    public function delete($id) {
        if (!@unlink($this->cacheDir . $id)) {
            throw new RuntimeException("Unable to delete the data with ID '$id'.");
        }
        return $this;
    }
    
    public function exists($id) {
        return file_exists($this->cacheDir . $id);
    }
}

The driving logic of the FileCache class should be easy to understand. By far the most relevant thing here is that it exposes a neat polymorphic behavior as it’s a faithful implementer of the earlier CacheInterface.

FileCache类的驱动逻辑应该很容易理解。 到目前为止,最相关的是它公开了一个整洁的多态行为,因为它是早期CacheInterface的忠实实现者。

Although this ability is sweet and charming, on its own it’s not precisely something I’d sing praise about considering that the goal here is to create a cache component capable of switching back-ends at runtime. Let’s make an extra effort on behalf of an instructive cause and bring to life yet another slim implementer of the CacheInterface.

尽管此功能既甜美又迷人,但就其目标是创建一个能够在运行时切换后端的缓存组件而言,我并不是要赞扬它。 让我们为一个有启发性的事业付出更多的努力,并使CacheInterface另一个苗条实现者CacheInterface

The implementation below adheres to the interface’s contract, but this time by consuming methods bundled with the APC extension:

下面的实现遵循接口的约定,但是这次使用了与APC扩展捆绑在一起的方法:

<?php
namespace LibraryCache;

class ApcCache implements CacheInterface
{
    public function set($id, $data, $lifeTime = 0) {
        if (!apc_store($id, $data, (int) $lifeTime)) {
            throw new RuntimeException("Unable to cache the data with ID '$id'.");
        }
    }
    
    public function get($id) {
        if (!$data = apc_fetch($id)) {
            throw new RuntimeException("Unable to get the data with ID '$id'.");
        } 
        return $data;
    }
    
    public function delete($id) {
        if (!apc_delete($id)) {
            throw new RuntimeException("Unable to delete the data with ID '$id'.");
        }
    }
    
    public function exists($id) {
        return apc_exists($id);
    }
}

The ApcCache class isn’t the flashiest APC wrapper you’ll see in your career, it packages all the functionality required for saving, retrieving, and removing data from memory.

ApcCache类并不是您在职业生涯中看到的最华丽的APC包装器,它封装了保存,检索和从内存中删除数据所需的所有功能。

Let’s pat ourselves on the back as we’ve managed to implement a lightweight cache module whose concrete back-ends not only can be swapped out easily at runtime thanks to their polymorphic nature, but adding a few more down the road is ridiculously simple. Just write another implementation that adheres to the CacheInterface.

我们设法实现了一个轻量级的缓存模块,它的具体后端不仅由于运行时的多态性而可以在运行时轻松换出,而且在途中添加了一些额外的方法也非常简单。 只需编写另一个遵循CacheInterface实现CacheInterface

I should stress however that actual subtype Polymorphism has been achieved by implementing a contract defined through an interface construct, which is a pretty ubiquitous approach. Nothing should stop you, however, from being less orthodox and getting the same results by switching over an interface declared as a set of abstract methods pitched inside an abstract class. If you’re feeling adventurous and want to walk that sideways path, the contract and the corresponding implementations could be refactored as follows:

但是,我要强调的是,实际的子类型多态已经通过实现通过接口构造定义的协定来实现,这是一种非常普遍的方法。 但是,通过切换一个声明为抽象类内部的抽象方法集合的接口,没有什么可以阻止您变得正统,并获得相同的结果。 如果您喜欢冒险并且想走这条路,可以按以下方式重构合同和相应的实现:

<?php
namespace LibraryCache;

abstract class AbstractCache
{
    abstract public function set($id, $data);
    abstract public function get($id);
    abstract public function delete($id);
    abstract public function exists($id);
}
<?php
namespace LibraryCache;

class FileCache extends AbstractCache
{
	// the same implementation goes here
}
<?php
namespace LibraryCache;

class ApcCache extends AbstractCache
{
	// the same implementation goes here 
}

From top to bottom, this is true a polymorphic approach that fights head to head with the one discussed before. Personally, and this is nothing but speaking for myself, I prefer to use interface constructs for defining contracts and appeal to abstract classes only when it comes to encapsulating a boilerplate implementation shared by a few subtypes. It’s up to you to pick the methodology that will best fit your needs.

从上到下,这确实是一种多态方法,它与前面讨论的方法并肩作战。 就我个人而言,这无非是对我自己说,我更喜欢使用接口构造来定义协定,并且仅在封装由几个子类型共享的样板实现时才吸引抽象类。 由您选择最适合您需求的方法。

At this point I could drop the curtain write some fancy closing comments, babbling on about our impressive coding skills and bragging about the flexibility of or cache component, but that would be plain a disservice. Polymorphism exposes its most seductive facet when there exists some client code capable of consuming several implementations without checking if they’re types of something as long as they adhere to an expected contract. So, let’s unveil that facet by hooking up the cache component to a basic client view class, something that will permit us do some neat HTML caching without much fuss.

在这一点上,我可以大声疾呼地写一些特别的总结性意见,对我们令人印象深刻的编码技巧bab之以鼻,对吹牛或缓存组件的灵活性自吹自but,但这显然是不利的。 当存在一些客户端代码能够使用多个实现而无需检查它们是否是某种类型的东西(只要它们遵守预期的合同)时,多态性就暴露出其最诱人的方面。 因此,让我们通过将缓存组件连接到基本的客户端视图类来揭示这一方面,这将使我们能够进行一些整洁HTML缓存而不必大惊小怪。

使缓存驱动程序正常工作 (Putting the Cache Drivers to Work)

Caching HTML output through our sample cache module is so banal, that I’ll save any verbose explanation for some other time. The whole caching process can be reduced to a naïve view class, similar to this one:

通过示例缓存模块缓存HTML输出是如此平庸,以至于我将在以后再保存任何详细的说明。 整个缓存过程可以简化为简单的视图类,类似于此视图类:

<?php
namespace LibraryView;

interface ViewInterface
{
    public function setTemplate($template);
    public function __set($field, $value);
    public function __get($field);
    public function render();
}
<?php
namespace LibraryView;
use LibraryCacheCacheInterface;

class View implements ViewInterface
{
    const DEFAULT_TEMPLATE = 'default';    
    private $template;
    private $fields = array();
    private $cache;

    public function __construct(CacheInterface $cache, $template = self::DEFAULT_TEMPLATE) {
        $this->cache = $cache;
        $this->setTemplate($template);
    }
    
    public function setTemplate($template) {
        $template = $template . '.php';
        if (!is_file($template) || !is_readable($template)) {
            throw new InvalidArgumentException(
                "The template '$template' is invalid.");   
        }
        $this->template = $template;
        return $this;
    }
    
    public function __set($name, $value) {
        $this->fields[$name] = $value;
        return $this;
    }
    
    public function __get($name) {
        if (!isset($this->fields[$name])) {
            throw new InvalidArgumentException(
                "Unable to get the field '$field'.");
        }
        return $this->fields[$name];
    }
    
    public function render() {
        try {
            if (!$this->cache->exists($this->template)) {
                extract($this->fields);
                ob_start();
                include $this->template;
                $this->cache->set($this->template, ob_get_clean());
            }
            return $this->cache->get($this->template);
        }
        catch (RuntimeException $e) {
            throw new Exception($e->getMessage());
        } 
    }
}

The flashiest kid on the block is the class’ constructor, which consumes an implementer of the earlier CacheInterface, and the render() method. Since the responsibility of this last one is caching the view’s template once it’s been pushed through the output buffer, it’d be pretty nice to exploit this ability and cache an entire HTML document.

块上最闪亮的孩子是类的构造函数,它使用了早期CacheInterfacerender()方法的实现。 由于这最后一个的职责是在视图模板被推入输出缓冲区后对其进行缓存,因此利用此功能并缓存整个HTML文档将是非常不错的。

Let’s say the view’s default template has the following structure:

假设视图的默认模板具有以下结构:

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <title>The Default Template</title>
 </head>
 <body>
  <header>
   <h1>You are viewing the default page!</h1>
<?php echo $this->header;?>
  </header>
  <section>
<?php echo $this->body;?>
  </section>
  <footer>
<?php echo $this->footer;?>
  </footer>
 </body>
</html>

Now, let’s have some fun and cache the document by feeding the view with an instance of the ApcCache class:

现在,让我们玩得开心,通过向视图提供ApcCache类的实例来缓存文档:

<?php
use LibraryLoaderAutoloader,
    LibraryCacheFileCache,
    LibraryCacheApcCache,
    LibraryViewView;
        
require_once __DIR__ . '/Library/Loader/Autoloader.php';
$autoloader = new Autoloader;
$autoloader->register();

$view = new View(new ApcCache());

$view->header = 'This is my fancy header section';
$view->body = 'This is my fancy body section';
$view->footer = 'This is my fancy footer section';

echo $view->render();

Pretty nice, right? But hang on just a moment! I got so caught up in the moment that I forgot to mention the above snippet will blow up on any system where the APC extension hasn’t been installed (naughty sysadmins!). Does that mean that the beautifully-crafted cache module isn’t reusable anymore?

很好,对吗? 但是请稍等! 我一时陷入困境,以至于忘记了上面的代码片段在尚未安装APC扩展程序的任何系统上都会崩溃(顽皮的系统管理员!)。 这是否意味着制作精美的缓存模块不再可重复使用?

Here’s precisely where the file-based driver comes to the rescue, which can be dropped into client code without getting a single compliant from it:

这正是基于文件的驱动程序的得力助手,可以将其放入客户端代码中而无需从中获得任何合规性:

<?php
$view = new View(new FileCache());

The above one-liner explicitly declares that the view will be caching its output using the file system instead of shared memory. This switching of cache back-ends on the fly shows in a nutshell why Polymorphism is so crucial when it comes to designing highly-decoupled modules. It allows us to easily rewire things at runtime without rippling fragility/rigidity-related artifacts to other parts of our system.

上面的单行代码明确声明该视图将使用文件系统而不是共享内存来缓存其输出。 高速缓存后端的这种切换简而言之说明了为什么在设计高度解耦的模块时,多态性如此重要。 它使我们能够在运行时轻松地重新连接事物,而不会将与脆弱性/刚性相关的构件荡漾到系统的其他部分。

结束语 (Closing Remarks)

Crushed under a overwhelming pile of formal definitions that make understanding the concept elusive, Polymorphism is really one of those beautiful things in life which, once you do understand it, makes you wonder how you could have gone on for so long without it.

多态性被压倒在使人们难以理解的形式定义的压倒性堆砌中,实际上是生活中那些美丽的事物之一,一旦您理解了它,就会使您想知道如果没有它,该怎么能持续这么久。

Polymorphic systems are by nature much more orthogonal, easier to extend, and a lot less vulnerable to infringing central paradigms such as the Open/Closed Principle and the wise “Programming to Interfaces” mantra. Although rather primitive, our cache module is a shining example of these virtues in action.

从本质上讲,多态系统具有更多的正交性,更易于扩展,并且不易受侵犯诸如开放/封闭原理和明智的“编程到接口”的口号等中心范式的侵害。 尽管相当原始,我们的缓存模块还是这些美德在行动中的光辉榜样。

If you haven’t refactored your applications yet so that they can take advantage of the benefits brought to the table by Polymorphism, you’d better hurry up because you’re missing the jackpot!

如果您尚未重构应用程序,以便它们可以利用Polymorphism带来的好处,那么最好赶快行动,因为您错过了大奖!

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/subtype-polymophism/

子类型和基类型转换

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值