meter mac
Software programming is a balanced mix of art (sometimes a euphemism for improvisation) and a bunch of well-proven heuristics used to tackle certain problems and solve them in a decent fashion. Few will disagree that the artistic side is by far the hardest one to polish and distill over time. On the other hand, taming the forces behind the heuristics is fundamental for being able to developing software that rests on the foundation of good design.
软件编程是艺术的平衡平衡(有时是委婉的委婉说法),还有一堆经过证明的启发式方法,用于解决某些问题并以体面的方式解决它们。 很少有人会认为艺术方面是迄今为止最难抛光和提炼的方面。 另一方面,驯服启发式方法背后的力量是能够开发基于良好设计基础的软件的基础。
With so many heuristics stating how and why software systems should cling to a specific approach, it’s pretty disappointing not seeing a broader implementation of them in the world of PHP. For example, the Law of Demeter is probably one of the most underrated in the language’s realm.
如此众多的启发式方法说明了软件系统应如何以及为何坚持使用特定的方法,令人失望的是,在PHP世界中没有看到它们的更广泛的实现。 例如,得墨meter耳定律可能是该语言领域中被低估的其中之一。
Effectively, the law’s “talk to your closest friends” mantra still seems to be in a pretty immature state in PHP, something that contributes to rot in the overall quality of several object-oriented code bases. Some popular frameworks are actively pushing it forward, trying to be more committed to the law’s commandments. Throwing blame around for infringing the Law of Demeter is pointless, as the best way to mitigate such breakages is to simply be pragmatic and understand what’s actually under the law’s hood hence consciously applying it when writing object-oriented code.
实际上,在PHP中,法律的“与最亲密的朋友交谈”的口号似乎仍然处于不成熟的状态,这导致一些面向对象的代码库的整体质量下降。 一些流行的框架正在积极地推动它向前发展,以更加忠实于法律的诫命。 谴责违反《得墨meter耳法则》是毫无意义的,因为减轻此类破坏的最佳方法就是简单地务实并了解法律的实质内容,因此在编写面向对象的代码时有意识地应用它。
In an attempt to join the just cause and dig a little bit deeper into the law from a practical point of view, in the next few lines I’ll be demonstrating through some hands-on examples why something so simple as adhering to the law’s principles can be a real boost when designing loosely-coupled software modules.
为了尝试加入正义事业并从实践的角度更深入地研究法律,在接下来的几行中,我将通过一些动手实例来说明为什么一些简单而又遵循法律原则的例子当设计松耦合软件模块时,可以真正提高。
Often referred to as the Principle of Least Knowledge, the rules promoted by the Law of Demeter are easy to digest. Simply put, and assuming that you have a beautifully-crafted class which implements a given method, the method in question should be constrained to call other methods that belong to the following objects:
得墨meter耳定律所提倡的规则通常被称为“最少知识原理”,很容易消化。 简而言之,假设您有一个精美的类来实现给定方法,则该方法应被约束为调用属于以下对象的其他方法:
An instance of the method’s originating class. 方法的原始类的实例。 Objects that are arguments of the target method. 作为目标方法的参数的对象。 Objects that are created by the target method. 由目标方法创建的对象。 Objects that are dependencies of the method’s originating class. 作为方法的原始类的依赖项的对象。 Global objects (ouch!) that can be accessed by the originating class within the target method. 可由目标方法中的原始类访问的全局对象(ouch!)。Although the list is a world away from being formal (for one that’s a little more formal, check out Wikipedia), the points are pretty easy to understand.
尽管列表离正式还差很多(要正式一点,请查阅Wikipedia ),但要点很容易理解。
In traditional design, the fact that an object knows way too much about another (and this implicitly includes knowing how to access a third one) is considered wrong because there are situations where the object has to unnecessarily traverse from top to bottom a clumsy mediator to find the actual dependencies it needs to work as expected. This is, for obvious reason, a serious design flaw. The caller has a pretty extensive and detailed knowledge about the mediator’s internal structure, even if this one is accessed through a few getters.
在传统设计中,一个对象对另一个对象的了解太多(这隐含了如何访问第三个对象)的事实被认为是错误的,因为在某些情况下,该对象必须不必要地从笨拙的介体到顶部遍历。找到它所需的实际依赖关系,以按预期方式工作。 出于明显的原因,这是一个严重的设计缺陷。 调用者对调解员的内部结构有相当广泛而详尽的知识,即使通过一些获取器也可以访问调解员的内部结构。
Moreover, using an intermediary object to get to the one required by the caller makes a statement on its own. After all, why use such a tangled path to acquire a dependency or invoke one of its methods if the same result can be achieved by injecting the dependency directly? The process doesn’t make any sense at all.
而且,使用中介对象来达到调用者所需的对象。 毕竟,如果可以通过直接注入依赖项来获得相同的结果,为什么还要使用这种纠结的路径来获取依赖项或调用其方法之一? 这个过程根本没有任何意义。
Let’s say we need to build up a file storage module which uses internally a polymorphic encoder to pull in and save data to a given target file. If we were intentionally sloppy and hooked up the module to an injectable service locator, its implementation would look like this:
假设我们需要构建一个文件存储模块,该模块在内部使用一个多态编码器来提取数据并将其保存到给定的目标文件中。 如果我们故意草率地将模块连接到可注入服务定位器,则其实现将如下所示:
<?php namespace LibraryFile; use LibraryDependencyInjectionServiceLocatorInterface; class FileStorage { const DEFAULT_STORAGE_FILE = "data.dat"; private $locator; private $file; public function __construct(ServiceLocatorInterface $locator, $file = self::DEFAULT_STORAGE_FILE) { $this->locator = $locator; $this->setFile($file); } public function setFile($file) { if (!is_readable($file) || !is_writable($file)) { throw new InvalidArgumentException( "The target file is invalid."); } $this->file = $file; return $this; } public function write($data) { try { return file_put_contents($this->file, $this->locator->get("encoder")->encode($data), LOCK_EX); } catch (Exception $e) { throw new $e( "Error writing data to the target file: " . $e->getMessage()); } } public function read() { try { return $this->locator->get("encoder")->decode( @file_get_contents($this->file)); } catch(Exception $e) { throw new $e( "Error reading data from the target file: " . $e->getMessage()); } } }Leaving out of the picture some irrelevant implementation details, the focus is on the constructor of the FileStorage class and its write() and read() methods. The class injects an instance of a still undefined service locator, which is used later on for acquiring a dependency (the aforementioned encoder) in order to fetch and store data in the target file.
省略了一些不相关的实现细节,重点是FileStorage类的构造函数及其write()和read()方法。 该类将注入一个仍未定义的服务定位符的实例,该实例稍后将用于获取依赖项(上述编码器),以便在目标文件中获取并存储数据。
This is a typical infringement of the Law of Demeter considering that the class first goes through the locator and in turn reaches the encoder. The caller FileStorage knows too much about the locator’s internals, including how to access the encoder, which definitively isn’t an ability I would sing praises about. It’s an artifact intrinsically rooted to the nature of service locators (and that’s why some see them as an anti-pattern) or any other kind of static or dynamic registries, something that I pointed out before.
考虑到该类首先通过定位器,然后到达编码器,这是对Demeter定律的典型侵犯。 调用者FileStorage对定位器的内部知识了解太多,包括如何访问编码器,这绝对不是我要赞扬的功能。 这是一种人工产物,本质上植根于服务定位器的性质(这就是为什么某些人将它们视为反模式)或任何其他种类的静态或动态注册表的原因,这是我之前指出的 。
To have a more general view of the issue, let’s check the locator’s implementation:
要对该问题有更一般的了解,让我们检查定位器的实现:
<?php namespace LibraryDependencyInjection; interface ServiceLocatorInterface { public function set($name, $service); public function get($name); public function exists($name); public function remove($name); public function clear(); } <?php namespace LibraryDependencyInjection; class ServiceLocator implements ServiceLocatorInterface { private $services = []; public function set($name, $service) { if (!is_object($service)) { throw new InvalidArgumentException( "Only objects can register with the locator."); } if (!in_array($service, $this->services, true)) { $this->services[$name] = $service; } return $this; } public function get($name) { if (!$this->exists($name)) { throw new InvalidArgumentException( "The requested service is not registered."); } return $this->services[$name]; } public function exists($name) { return isset($this->services[$name]); } public function remove($name) { if (!$this->exists($name)) { throw new InvalidArgumentException( "The requested service is not registered."); } unset($this->services[$name]); return $this; } public function clear() { $this->services = []; return $this; } }In this case I implemented the locator as a plain dynamic registry with no additional bells or whistles so it’s easy to follow. You can decorate it with some extra functionality if you’re in the mood.
在这种情况下,我将定位器实现为一个普通的动态注册表,没有任何额外的提示,因此很容易遵循。 如果您愿意,可以使用一些额外的功能来装饰它。
The last thing we must do is create at least one concrete implementation of the corresponding encoder so that we can put the file storage class to work. This class should do the trick pretty nicely:
我们必须做的最后一件事是为相应的编码器创建至少一个具体的实现,以便我们可以使文件存储类正常工作。 此类应该可以很好地完成技巧:
<?php namespace LibraryEncoder; interface EncoderInterface { public function encode($data); public function decode($data); } <?php namespace LibraryEncoder; class Serializer implements EncoderInterface { public function encode($data) { if (is_resource($data)) { throw new InvalidArgumentException( "PHP resources are not serializable."); } if (($data = serialize($data)) === false) { throw new RuntimeException( "Unable to serialize the data."); } return $data; } public function decode($data) { if (!is_string($data)|| empty($data)) { throw new InvalidArgumentException( "The data to be unserialized must be a non-empty string."); } if (($data = @unserialize($data)) === false) { throw new RuntimeException( "Unable to unserialize the data."); } return $data; } }With the encoder set, now let’s get things rolling using all the sample classes together:
设置好编码器后,现在让我们一起使用所有示例类来进行滚动:
<?php use LibraryLoaderAutoloader, LibraryEncoderSerializer, LibraryDependencyInjectionServiceLocator, LibraryFileFileStorage; require_once __DIR__ . "/Library/Loader/Autoloader.php"; $autoloader = new Autoloader(); $autoloader->register(); $locator = new ServiceLocator(); $locator->set("encoder", new Serializer()); $fileStorage = new FileStorage($locator); $fileStorage->write(["This", "is", "my", "sample", "array"]); print_r($fileStorage->read());The violation of the law is in this case a rather furtive issue hard to track down from the surface except for the use of the locator’s mutator which suggests that at some point the encoder will be accessed and consumed in some form by an instance of FileStorage. Regardless, we know the infringement is just right there hidden from the outside world, a fact that not only reveals too much about the locator’s structure, but couples unnecessarily the FileStorage class to the locator itself.
在这种情况下,违反法律是一个相当隐蔽的问题,除了使用定位器的mutator之外,很难从表面进行跟踪,这表明在某个时候FileStorage实例将以某种形式访问和使用编码器。 无论如何,我们都知道侵权就在外面,这是一个隐蔽的事实,这一事实不仅揭示了定位器的太多结构,而且不必要地将FileStorage类与定位器本身耦合在一起。
Just by sticking to the law’s rules and getting rid of the locator, we’d be removing the coupling, while at the same providing FileStorage with the actual collaborator it needs to do its business. No more clunky, revealing mediators along the way!
只需遵守法律规则并摆脱定位器,我们就可以消除耦合,同时为FileStorage提供开展业务所需的实际协作者。 一路走来,不再笨拙,透露中介者!
Fortunately, all this babble can be easily translated into working code with just a pinch of effort. Just check the enhanced, Law of Demeter-compliant version of the FileStorage class here:
幸运的是,只需花些力气就可以轻松地将所有这些胡言乱语转换为工作代码。 只需在此处检查FileStorage类的增强的,符合Demeter律的版本:
<?php namespace LibraryFile; use LibraryEncoderEncoderInterface; class FileStorage { const DEFAULT_STORAGE_FILE = "data.dat"; private $encoder; private $file; public function __construct(EncoderInterface $encoder, $file = self::DEFAULT_STORAGE_FILE) { $this->encoder = $encoder; $this->setFile($file); } public function setFile($file) { // the sample implementation } public function write($data) { try { return file_put_contents($this->file, $this->encoder->encode($data), LOCK_EX); } catch (Exception $e) { throw new $e( "Error writing data to the target file: " . $e->getMessage()); } } public function read() { try { return $this->encoder->decode( @file_get_contents($this->file)); } catch(Exception $e) { throw new $e( "Error reading data from the target file: " . $e->getMessage()); } } }That was easy to refactor, indeed. Now the class directly consumes any implementers of the EncoderInterface interface, avoiding going through the internals of an unnecessary intermediate. The example is unquestionably trivial, but it does make a valid point and demonstrates why adhering to the Law of Demeter’s commandments is one of the best things you can do to improve the design of your classes.
确实,这很容易重构。 现在,该类直接使用EncoderInterface接口的所有实现程序,从而避免遍历不必要的中间组件的内部。 该示例无疑是微不足道的,但它确实提出了一个正确的观点,并说明了为什么遵守德米特律法则是改善类设计的最佳方法之一。
Still, there’s a special case of the law, covered in depth in Robert Martin’s book Clean Code: A Handbook of Agile Software Craftsmanship, that deserves a particular analysis. Just think this through for a moment: what would happen if FileStorage was defined to acquire its collaborator via a Data Transfer Object (DTO), like this?
尽管如此,在罗伯特·马丁(Robert Martin)的著作《清洁代码: 敏捷软件手艺手册 》中深入探讨了法律的特殊情况,值得进行特殊分析。 请仔细考虑一下:如果将FileStorage定义为通过数据传输对象(DTO)来获取其协作者,将会发生什么?
<?php namespace LibraryFile; interface FileStorageDefinitionInterface { public function getEncoder(); public function getFile(); } <?php namespace LibraryFile; use LibraryEncoderEncoderInterface; class FileStorageDefinition implements FileStorageDefinitionInterface { const DEFAULT_STORAGE_FILE = "data.dat"; private $encoder; private $file; public function __construct(EncoderInterface $encoder, $file = self::DEFAULT_STORAGE_FILE) { if (!is_readable($file) || !is_writable($file)) { throw new InvalidArgumentException( "The target file is invalid."); } $this->encoder = $encoder; $this->file = $file; } public function getEncoder() { return $this->encoder; } public function getFile() { return $this->file; } } <?php namespace LibraryFile; class FileStorage { private $storageDefinition; public function __construct(FileStorageDefinitionInterface $storageDefinition) { $this->storageDefinition = $storageDefinition; } public function write($data) { try { return file_put_contents( $this->storageDefinition->getFile(), $this->storageDefinition->getEncoder()->encode($data), LOCK_EX ); } catch (Exception $e) { throw new $e( "Error writing data to the target file: " . $e->getMessage()); } } public function read() { try { return $this->storageDefinition->getEncoder()->decode( @file_get_contents($this->storageDefinition->getFile()) ); } catch(Exception $e) { throw new $e( "Error reading data from the target file: " . $e->getMessage()); } } }It’s definitely an interesting slant for implementing the file storage class as it now uses an injectable DTO for transferring and consuming internally the encoder. The question that begs answering is if this approach really violates the law. In a purist sense it does, as the DTO is unquestionably a mediator exposing its whole structure to the caller. However, the DTO is just a plain data structure which, unlike the earlier service locator, has no behavior at all. And precisely the purpose of data structures is… yes, to expose its data. This means that as long as the mediator doesn’t implement behavior (which is exactly the opposite to what a regular class does, as it exposes behavior while hiding its data), the Law of Demeter will remain neatly preserved.
实现文件存储类绝对是一个有趣的倾向,因为它现在使用可注入的DTO在内部传输和使用编码器。 要求回答的问题是这种方法是否确实违反法律。 从纯粹的意义上讲,它确实如此,因为DTO无疑是一个调解员,将其整个结构公开给调用者。 但是,DTO只是一个普通的数据结构,与早期的服务定位器不同,它根本没有任何行为。 数据结构的确切目的是……是的,公开其数据。 这意味着只要调解器不执行行为(这与常规类的行为完全相反,因为它在隐藏数据的同时公开了行为),则得墨meter耳的定律将保持整洁。
The following snippet shows how to use the FileStorage with the DTO in question:
以下代码片段显示了如何将FileStorage与FileStorage一起使用:
<?php $fileStorage = new FileStorage(new FileStorageDefinition(new Serializer())); $fileStorage->write(["This", "is", "my", "sample", "array"]); print_r($fileStorage->read());This approach is a lot more cumbersome than just directly passing the encoder into the file storage class, but the example shows that some tricky implementations, which at first blush seem to be flagrant breakers of the law, are, in general, pretty harmless as long as they make use of data structures with no behavior attached to them.
这种方法比直接将编码器直接传递到文件存储类要麻烦得多,但是该示例表明,一些棘手的实现通常乍看之下对法律无害,但乍一看似乎很违法。因为它们利用了没有附加任何行为的数据结构。
With a prolific variety of tangled, sometimes esoteric, heuristics making their way through OOP, it seems pointless to add just another one to the pile, which apparently doesn’t have any visible positive impact in the design of layer components. The Law of Demeter, though, is everything but a principle with little or no application in the real world.
杂乱无章的,有时是深奥的启发式方法在OOP中一路走来,在堆中添加另一种似乎毫无意义,这显然对层组件的设计没有任何明显的积极影响。 然而,得墨meter耳定律只是一切,而在现实世界中几乎没有或根本没有应用。
Despite of its flourishy name, the Law of Demeter is a powerful paradigm whose primary goal is to promote the implementation of highly-decoupled application components by eliminating any unnecessary mediators. Just follow its commandments, without falling into blind dogmatism of course, and you’ll see the quality of your code improve. Guaranteed.
尽管得名,但Demeter是一个强大的范例,其主要目标是通过消除任何不必要的介体来促进高度分离的应用程序组件的实现。 只需遵循其诫命,当然也不会陷入盲目教条主义,您会发现代码质量得到了提高。 保证。
Image via Fotolia
图片来自Fotolia
翻译自: https://www.sitepoint.com/introduction-to-the-law-of-demeter/
meter mac
相关资源:jdk-8u281-windows-x64.exe