虚拟机 全局代理 主机代理

tech2023-11-13  89

虚拟机 全局代理 主机代理

One of the most appealing facets of Polymorphism is that it is applicable in a wide variety of situations. Moreover, while the ability to switch out concrete implementations is in and of itself a fundamental object-oriented pillar, commonly used for creating loosely-coupled, interchangeable components, its usage falls shy when it comes to pulling on the reins of domain objects.

多态性最吸引人的方面之一是它适用于多种情况。 而且,尽管切换出具体实现的能力本身就是一个基本的面向对象的Struts,通常用于创建松耦合,可互换的组件,但是在牵扯领域对象的束缚时,它的使用却显得有些害羞。

There’s a somewhat fragile rationale that stands behind this observation: in many cases, domain objects are conceptually modelled up front to operate on concrete sets of data and inside the boundaries of strict relationships which most likely won’t change over time, and where most of the varying business logic is placed in hierarchies of standalone strategy classes (here’s where actual Polymorphism takes place). In such cases, certainly there’s not much room or even compelling reasons to justify having different implementations of domain objects at runtime except for mocking/testing.

该观察结果背后存在一个脆弱的理由:在许多情况下,领域对象在概念上是预先建模的,以在具体的数据集上和严格关系的边界内操作,这些关系很可能不会随时间变化,并且在大多数情况下不同的业务逻辑放置在独立策略类的层次结构中(此处是实际的多态发生的地方)。 在这种情况下,除了模拟/测试之外,没有足够的空间甚至是令人信服的理由来证明在运行时具有不同的域对象实现。

Furthermore, very few will disagree that programming to interfaces is a bad thing, but isn’t it overkill to have one per domain object? After all, a user object will always be modelled with a few typical roles in mind, and if its login() method ever needs to be updated, well… it’ll just be refactored accordingly and the client code won’t complain so long as the mutual contract is maintained. At a glance, it seems that having a whole new user implementation not only isn’t very pragmatic, but it’s simply absurd.

此外,很少有人会反对对接口进行编程是一件坏事,但是每个域对象都拥有一个接口不是太过分了吗? 毕竟,一个用户对象在建模时总是会考虑一些典型的角色,如果需要更新它的login()方法,那么……它将进行相应的重构,并且客户端代码不会抱怨那么久。因为双方之间的合同得以维持。 乍一看,拥有一个全新的用户实现不仅不十分实用,而且简直荒谬。

A common pitfall with this approach is made apparent when it’s necessary to pull in an aggregate (a domain object made up of other objects or collections) from the database. As each reference to the aggregate implies dealing face to face with the real domain objects, the process unavoidably ends up dropping the entire object graph into the client. It’s not exactly a solution to praise with fervency, even if the objects can be cached later on.

当有必要从数据库中引入聚合(由其他对象或集合组成的域对象)时,使用这种方法的常见陷阱就显而易见了。 由于每个对聚合的引用都意味着要与真实域对象面对面地进行处理,因此该过程不可避免地最终将整个对象图放入客户端。 即使对象以后可以被缓存,这也不是一个以夸张的方式赞美的解决方案。

As usual, simple solutions are the most effective ones, and this applies to the above problem. Rather than fetching the real fat aggregate, if a lightweight substitute is used instead which shares the same interface and knows how to get the aggregate in question from storage then lazy-loading the underlying objects becomes a straightforward process. Often referenced by a few other fancy names, the substitute is generically called a virtual proxy, a sort of stand-in that exploits the neatness of Polymorphism and interacts with the actual domain objects.

通常,简单的解决方案是最有效的解决方案,这适用于上述问题。 如果使用轻量级替代品共享相同的接口,并且知道如何从存储中获取有问题的聚合,那么延迟加载而不是获取实际的脂肪聚合,则延迟加载基础对象将成为一个简单的过程。 通常由其他一些奇特的名字引用,它通常被称为虚拟代理 ,它是一种利用多态性的整洁并与实际域对象进行交互的替身。

Proxies aren’t new to PHP. Doctrine and Zend Framework 2.x make use of them, although with different aims. On behalf of a didactic cause, however, it would be pretty instructive to implement some custom proxy classes and use them for lazy-loading a few basic aggregates from the database, this way illustrating how virtual proxies do their stuff under the hood.

代理对于PHP来说并不陌生。 尽管目标不同,但Doctrine和Zend Framework 2.x还是利用了它们。 但是,就说教原因而言,实现一些自定义代理类并将其用于从数据库中延迟加载一些基本聚合是非常有启发性的,这种方式说明了虚拟代理如何在后台进行工作。

Considering the functionality of proxies can be easily accommodated to coordinate either with single domain objects or with collections of them (or both), in the first part of this two-part series I’ll be showcasing the former user case, while in the second installment I’ll dig deeper into the complexities of the latter.

考虑到代理的功能可以轻松地与单个域对象或它们(或两者)的集合进行协调,在这个由两部分组成的系列的第一部分中,我将展示前一种用例,而在第二部分中我将更深入地探讨后者的复杂性。

So, let’s now move along and get things finally rolling with virtual proxies.

因此,现在让我们继续前进,并最终通过虚拟代理实现一切。

设置域模型 (Setting Up a Domain Model)

As I pointed out in the introduction, proxies are simple -yet powerful- structures that allow you to pull in requested domain objects from underlying storage. But as one might expect, it would first be warranted to create a simple Domain Model so at least one of its building objects can be interfaced to a proxy.

正如我在简介中所指出的,代理是简单但功能强大的结构,可让您从基础存储中提取请求的域对象。 但是,正如人们可能期望的那样,首先应保证创建一个简单的域模型,以便至少一个构建对象可以与代理接口。

The fist domain class I’ll add to the sample model will be one that represents blog posts. The class, along with its segregated interface, look like this:

我将添加到示例模型中的第一个域类将是代表博客文章的类。 该类及其隔离的接口如下所示:

<?php namespace Model; interface PostInterface { public function setId($id); public function getId(); public function setTitle($title); public function getTitle(); public function setContent($content); public function getContent(); public function setAuthor(AuthorInterface $author); public function getAuthor(); } <?php namespace Model; class Post implements PostInterface { protected $id; protected $title; protected $content; protected $author; public function __construct($title, $content, AuthorInterface $author) { $this->setTitle($title); $this->setContent($content); $this->setAuthor($author); } public function setId($id) { if ($this->id !== null) { throw new BadMethodCallException( "The ID for this post has been set already."); } if (!is_int($id) || $id < 1) { throw new InvalidArgumentException( "The post ID is invalid."); } $this->id = $id; return $this; } public function getId() { return $this->id; } public function setTitle($title) { if (!is_string($title) || strlen($title) < 2 || strlen($title) > 100) { throw new InvalidArgumentException( "The post title is invalid."); } $this->title = htmlspecialchars(trim($title), ENT_QUOTES); return $this; } public function getTitle() { return $this->title; } public function setContent($content) { if (!is_string($content) || strlen($content) < 2) { throw new InvalidArgumentException( "The post content is invalid."); } $this->content = htmlspecialchars(trim($content), ENT_QUOTES); return $this; } public function getContent() { return $this->content; } public function setAuthor(AuthorInterface $author) { $this->author = $author; return $this; } public function getAuthor() { return $this->author; } }

The behavior of the Post class is trivial as it just implements a few mutators/accessors with some basic filtering/validation. Notice, however, that the class injects an author’s implementation in the constructor, that way setting a one-to-one relationship with the corresponding author.

Post类的行为很简单,因为它仅实现了一些带有一些基本过滤/验证功能的mutators / accessor。 但是请注意,该类将作者的实现注入构造函数中,从而与相应的作者设置一对一关系。

To get the domain model up and running, the author in question must be modeled as well. Here’s the related class, along with its contract:

为了使域模型正常运行,必须对所涉及的作者进行建模。 这是相关的类及其合同:

<?php namespace Model; interface AuthorInterface { public function setId($id); public function getId(); public function setName($name); public function getName(); public function setEmail($email); public function getEmail(); } <?php namespace Model; class Author implements AuthorInterface { protected $id; protected $name; protected $email; public function __construct($name, $email) { $this->setName($name); $this->setEmail($email); } public function setId($id) { if ($this->id !== null) { throw new BadMethodCallException( "The ID for this author has been set already."); } if (!is_int($id) || $id < 1) { throw new InvalidArgumentException( "The author ID is invalid."); } $this->id = $id; return $this; } public function getId() { return $this->id; } public function setName($name) { if (strlen($name) < 2 || strlen($name) > 30) { throw new InvalidArgumentException( "The name of the author is invalid."); } $this->name = htmlspecialchars(trim($name), ENT_QUOTES); return $this; } public function getName() { return $this->name; } public function setEmail($email) { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException( "The email of the author is invalid."); } $this->email = $email; return $this; } public function getEmail() { return $this->email; } }

The same that I just said about the Post class applies to its collaborator, Author. There subtle detail worth stressing here is this: if the domain model were put to roll down the road right now, then any post object pulled in from the database would get an author object eagerly attached to it, something that fully honors its condition of aggregate.

我刚才对Post类说的内容也适用于其协作者Author 。 这里需要强调一些细微的细节:如果现在将领域模型推向前进的道路,那么从数据库中拉入的任何post对象都将急切地连接到author对象,这完全符合其汇总条件。

While this is all well and fine since authors aren’t expensive to set per se, there might be times when they could be loaded only on request. If this happens, all the last-minute retrieval logic should be placed outside the domain objects’ confines, but at the same time controlled by an object that understands the model.

虽然这一切都很好,因为作者本身的设置并不昂贵,但有时可能只能根据请求加载它们。 如果发生这种情况,则所有最新的检索逻辑都应放置在域对象的范围之外,但同时由理解模型的对象控制。

There’s no need to sink in the waters of anguish here, as this apparently-complex condition has a dead simple solution: if an author proxy is used instead of a real author, then it’s feasible to lazy-load the latter without spoiling the contract with client code since the proxy exposes the same API. This itself proves Polymorphism is hard to knock when it comes to swapping out domain object implementations in order to optimize your database persistence/retrieval strategy.

这里没有必要陷入痛苦之中,因为这种看似复杂的情况有一个死的简单解决方案:如果使用作者代理代替真正的作者,则可以懒加载后者而不破坏合同。客户端代码,因为代理公开了相同的API。 这本身证明,在为优化数据库持久性/检索策略而换出域对象实现时,很难克服多态性。

Curiosity is a pretty itching bug indeed, so let’s satisfy our own one and see how to create the above mentioned author proxy.

好奇心确实是一个非常棘手的错误,所以让我们满足自己的一个,看看如何创建上述作者代理。

通过虚拟代理根据请求加载作者 (Loading Authors on Request via Virtual Proxies)

Considering the proxy should be able to fetch author objects from the database, a neat way to do so would be through the API of a data mapper. In this particular case, the one shown below will get the job done nicely:

考虑到代理应该能够从数据库中获取作者对象,一种简洁的方法是通过数据映射器的API。 在这种特殊情况下,下面显示的将很好地完成工作:

<?php namespace ModelMapper; interface AuthorMapperInterface { public function fetchById($id); } <?php namespace ModelMapper; use LibraryDatabaseDatabaseAdapterInterface, ModelAuthor; class AuthorMapper implements AuthorMapperInterface { protected $entityTable = "authors"; public function __construct(DatabaseAdapterInterface $adapter) { $this->adapter = $adapter; } public function fetchById($id) { $this->adapter->select($this->entityTable, array("id" => $id)); if (!$row = $this->adapter->fetch()) { return null; } return new Author($row["name"], $row["email"]); } }

So far, so good. With the user mapper already in place, it’s time to tackle the creation of the author proxy. As stated before, it shares the same interface with the one implemented by real authors, and its core implementation is as follows:

到目前为止,一切都很好。 有了用户映射器之后,就该着手创建作者代理了。 如前所述,它与真实作者实现的接口具有相同的接口,其核心实现如下:

<?php namespace ModelProxy; use ModelMapperAuthorMapperInterface, ModelAuthorInterface; class AuthorProxy implements AuthorInterface { protected $author; protected $authorId; protected $authorMapper; public function __construct($authorId, AuthorMapperInterface $authorMapper) { $this->authorId = $authorId; $this->authorMapper = $authorMapper; } public function setId($id) { $this->authorId = $id; return $this; } public function getId() { return $this->authorId; } public function setName($name) { $this->loadAuthor(); $this->author->setName($name); return $this; } public function getName() { $this->loadAuthor(); return $this->author->getName(); } public function setEmail($email) { $this->loadAuthor(); $this->author->setEmail($email); return $this; } public function getEmail() { $this->loadAuthor(); return $this->author->getEmail(); } protected function loadAuthor() { if ($this->author === null) { if(!$this->author = $this->authorMapper->fetchById($this->authorId)) { throw new UnexpectedValueException( "Unable to fetch the author."); } } return $this->author; } }

At a quick glance the AuthorProxy class looks like a cheap and dirty duplicate of Author with little or no extra functionality, but I assure you this is nothing but a shallow impression. When analyzed it shows the logic that stands behind a virtual proxy in a nutshell. The loadAuthor() method is the proxy’s actual workhorse, as its responsibility is to fetch from the database once, and only once, an author object is injected into the constructor. The batch of additional methods are just wrappers for the author’ setters/getters.

乍一看, AuthorProxy类看起来像是Author的廉价且肮脏的副本,几乎没有或没有任何附加功能,但是我向您保证,这不过是一个简单的印象。 进行分析时,它概括地显示了虚拟代理背后的逻辑。 loadAuthor()方法是代理的实际工作方式,因为它的责任是从数据库中获取一次,并且仅一次将author对象注入到构造函数中。 这批其他方法只是作者的setter / getter的包装器。

All in all it should be clear that constructing a proxy class that transparently handles a few domain objects behind the scenes is a lot more approachable than one might think. Even so, the best way for catching up the proxy’s real strength is by example. Let’s suppose we need to build an application that fetches a few popular quotes from a database and then dumps them to screen along with their corresponding author.

总而言之,应该清楚的是,构造一个可以透明地处理某些领域对象的代理类比人们想像的要容易得多。 即便如此,赶超代理人真正实力的最佳方法还是举例。 假设我们需要构建一个应用程序,该应用程序从数据库中获取一些受欢迎的报价,然后将其与相应的作者一起转储到屏幕上。

In a simple implementation, the application would look pretty much like this:

在一个简单的实现中,该应用程序看起来非常像这样:

<?php use LibraryLoaderAutoloader, LibraryDatabasePdoAdapter, ModelMapperAuthorMapper, ModelProxyAuthorProxy, ModelAuthor, ModelPost; require_once __DIR__ . "/Library/Loader/Autoloader.php"; $autoloader = new Autoloader; $autoloader->register(); $adapter = new PdoAdapter("mysql:dbname=mydatabase", "dbuser", "dbpassword"); $authorMapper = new AuthorMapper($adapter); $author = $authorMapper->fetchById(1); $post = new Post( "About Men", "Men are born ignorant, not stupid; they are made stupid by education.", $author); echo $post->getTitle() . $post->getContent() . " Quote from: " . $post->getAuthor()->getName();

Even when the quotes’ mapper has been excluded for the sake of brevity, the code’s flow is still pretty easy to understand: it pulls in the first author object (a reference to Bertrand Russell) from the database which gets injected into the quote object. Here the author has been eagerly fetched from the storage before having any chances to process it, which isn’t a cardinal sin by the way.

即使为简洁起见,将引号的映射器排除在外,代码的流程也仍然很容易理解:它从数据库中提取第一个作者对象(对Bertrand Russell的引用),该对象被注入到引号对象中。 在这里,在没有任何机会处理存储库之前,作者已经急切地从存储库中取出了它,这并不是一个主要的罪过。

The purist madman living inside our heads can’t just be tossed aside so easily, though, and keeps murmuring how the author would be better off lazy-loaded. In such a case, we could switch over the proxy instead, and drop it into the application, as follows:

但是,生活在我们脑海中的纯粹疯子不能轻易被抛在一边,并且一直在抱怨作者如何更好地摆脱懒惰。 在这种情况下,我们可以切换代理,然后将其放入应用程序中,如下所示:

<?php $author = new AuthorProxy(1, new AuthorMapper($adapter)); $post = new Post( "About Men", "Men are born ignorant, not stupid; they are made stupid by education.", $author); echo $post->getTitle() . $post->getContent() . " Quote from: " . $post->getAuthor()->getName();

If you take a close look at the line responsible for creating the post object, you’ll notice that it remains exactly the same as the one from before even though it now takes the proxy and lazy-loads the author from the database. Asides from illustrating how to make use of virtual proxies in a common use case, the example here shows how to keep things copesetic with client code as well. It sticks to the Open/Closed principle and relies entirely on a few segregated interfaces rather than on concrete implementations.

如果仔细查看负责创建post对象的那一行,您会发现它与以前的对象完全相同,即使现在它使用代理并从数据库中延迟加载了作者。 除了说明如何在常见用例中使用虚拟代理之外,此处的示例还显示了如何使事物与客户端代码保持一致。 它坚持开放/封闭原则,完全依赖于几个单独的接口,而不是具体的实现。

To sum things up: if you’re still wondering if Polymorphism has a place when it comes to creating domain objects that can be swapped up at runtime by virtual proxies (or something along that line), then rest assured it can help you build up scalable, future-proof domain models.

总结一下:如果您仍然想知道多态性在创建可以在运行时被虚拟代理(或类似方式)交换的域对象时是否占有一席之地,那么请放心,它可以帮助您建立可扩展的,面向未来的领域模型。

总结思想 (Closing Thoughts)

Virtual proxies come in a wide variety of flavors and forms, hence there’s no shortage of options when it comes to exploiting their functionality right out of the box. In many cases, they’re used as placeholders for actual domain objects that are rather expensive to create ahead in time, which makes it possible to load them transparently on request from the persistence layer without poisoning your client code with harmful conditionals.

虚拟代理具有多种风格和形式,因此,在开箱即用时充分利用虚拟代理功能是不二选择。 在许多情况下,它们被用作实际域对象的占位符,而这些对象要提前创建就非常昂贵,这使得可以根据持久层的要求透明地加载它们,而不会使您的客户端代码受到有害条件的毒害。

In the earlier example, a basic proxy was utilized for fetching a simple aggregate from a database, something hopefully instructive in the end, but not entirely in line with the real world. In more realistic situations, domain models are usually much fatter than that and are generally plagued by itteratable collections of domain objects.

在较早的示例中,使用了一个基本代理来从数据库中获取一个简单的聚合,这最终有望起到指导作用,但并不完全符合实际情况。 在更现实的情况下,领域模型通常比这要胖得多,并且通常受到领域对象令人讨厌的集合的困扰。

Not surprisingly, virtual proxies can be implemented for interplaying with collections as well without much fuss. So, in the next part I’ll show you how to create a collection-targeted proxy class so you can see for yourself if it fits your needs.

毫不奇怪,虚拟代理也可以实现与集合之间的交互,而不必大惊小怪。 因此,在下一部分中,我将向您展示如何创建一个以集合为目标的代理类,以便您自己查看它是否适合您的需求。

Image via imredesiuk / Shutterstock

图片来自imredesiuk / Shutterstock

翻译自: https://www.sitepoint.com/intro-to-virtual-proxies-1/

虚拟机 全局代理 主机代理

相关资源:jdk-8u281-windows-x64.exe
最新回复(0)