flyweight模式

tech2022-09-05  131

flyweight模式

The flyweight pattern is a relatively unknown design pattern in PHP. The fundamental principle behind the flyweight pattern is that memory can be saved by remembering objects after they have been created. Then, if the same objects need to be used again, resources do not have to be wasted recreating them.

flyweight模式是PHP中相对未知的设计模式。 flyweight模式背后的基本原理是可以通过在创建对象后记住对象来节省内存。 然后,如果需要再次使用相同的对象,则不必浪费资源来重新创建它们。

You can think of the flyweight pattern as a modification to a conventional object factory. The modification being that rather than just creating new objects all the time, the factory should check to see if it has already created the requested object. If it has, it should return this instance rather than create the object again.

您可以将flyweight模式视为对常规对象工厂的修改。 所做的修改是,工厂不仅应始终创建新对象,还应检查它是否已经创建了所请求的对象。 如果有,则应返回该实例,而不是再次创建该对象。

A good use case for the flyweight pattern would be an application that has to load large files. These files would be our flyweight objects.

flyweight模式的一个很好的用例是必须加载大文件的应用程序。 这些文件将成为我们的flyweight对象。

轻量级物体 (The Flyweight Object)

One important feature of flyweight objects is that they are immutable. This means that they cannot be changed once they have been constructed. This is because our factory can only guarantee that it has remembered the correct object if it can also guarantee that the object it originally created has not been modified.

轻量级物体的一个重要特征是它们是不可变的 。 这意味着它们一旦构建便无法更改。 这是因为,如果我们的工厂还可以保证最初创建的对象没有被修改,则只能保证它记住了正确的对象。

Below is a very simple example flyweight object for a file. We can tell that it is immutable because the ‘data’ property cannot be changed after the constructor has been called. There is no ‘setData’ method.

下面是一个非常简单的示例举重对象文件。 我们可以说它是不可变的,因为调用构造函数后不能更改'data'属性。 没有“ setData”方法。

class File { private $data; public function __construct($filePath) { // Check to make sure the file exists if (!file_exists($filePath)) { throw new InvalidArgumentException('File does not exist: '.$filePath); } $this->data = file_get_contents($filePath); } public function getData() { return $this->data; } }

轻量级工厂 (The Flyweight Factory)

Our flyweight factory needs to be able to obtain a unique identifier for the flyweight objects that it creates. It can then use this unique identifier to check if it has already created the object that it is being asked for. In many scenarios, the parameter(s) to our factory method will form an appropriate unique identifier for our flyweight; as it should be the case that if we call our factory with the same parameters, it will produce the same result.

我们的flyweight工厂需要能够为其创建的flyweight对象获取唯一的标识符。 然后,它可以使用此唯一标识符来检查它是否已经创建了要查找的对象。 在许多情况下,我们工厂方法的参数将为我们的飞行重量形成适当的唯一标识符; 因为如果我们用相同的参数调用工厂,将会产生相同的结果。

In the example below, we use the ‘files’ property as an associative array to store the objects that have been created. The most obvious choice for the unique identifier is the file path, so this will be the key to the elements in the array.

在下面的示例中,我们将'files'属性用作关联数组来存储已创建的对象。 唯一标识符最明显的选择是文件路径,因此这将是数组中元素的关键。

class FileFactory { private $files = array(); public function getFile($filePath) { // If the file path isn't in our array, we need to create it if (!isset($this->files[$filePath])) { $this->files[$filePath] = new File($filePath); } return $this->files[$filePath]; } }

Now we can use the FileFactory class to obtain files without worrying about loading them multiple times!

现在,我们可以使用FileFactory类获取文件,而不必担心会多次加载它们!

$factory = new FileFactory; $myLargeImageA = $factory->getFile('/path/to/my/large/image.png'); $myLargeImageB = $factory->getFile('/path/to/my/large/image.png'); if ($myLargeImageA === $myLargeImageB) { echo 'Yay, these are the same object!'.PHP_EOL; } else { echo 'Something went wrong :('.PHP_EOL; }

线程注意事项 (A Note on Threading)

The flyweight pattern is particularly useful in a multi-threaded environment. The immutability of flyweight objects guarantees that they can be used safely at the same time by multiple threads. This is because there is no chance that two threads will try to change the same object simultaneously.

轻量级模式在多线程环境中特别有用。 flyweight对象的不变性保证了多个线程可以同时安全地使用它们。 这是因为两个线程不可能同时尝试更改同一对象。

If you are using the flyweight pattern in an environment that is multi-threaded, you should consider the factory method as a critical section. Locks or similar controls should be used to ensure that two threads do not try to create an object at the same time.

如果在多线程环境中使用flyweight模式,则应将工厂方法视为关键部分。 应该使用锁或类似控件来确保两个线程不会尝试同时创建一个对象。

思考PHP (Thinking PHP)

Earlier I mentioned that the flyweight pattern is a relatively unknown design pattern in PHP. One of the main reasons for this is that memory usage is not a consideration for many PHP developers. Most of our PHP applications don’t have to do much work and their instances only live for a few milliseconds whilst they process the incoming HTTP request.

前面我提到过,flyweight模式是PHP中相对未知的设计模式。 这样做的主要原因之一是许多PHP开发人员都不考虑内存使用情况。 我们的大多数PHP应用程序都不需要做很多工作,并且它们的实例在处理传入的HTTP请求时仅存活几毫秒。

If, however, we are using the flyweight pattern in a long running application, we need to be aware of memory leaks. In PHP, memory will remain allocated to an object as long as references to it exist within the scope of the application. As a flyweight factory keeps references to every object it creates, when the number of objects that it could be asked to create is infinite, the application will eventually run out of memory.

但是,如果我们在运行时间较长的应用程序中使用flyweight模式,则需要注意内存泄漏。 在PHP中,只要在应用程序范围内存在对对象的引用,内存将一直分配给该对象。 当重量级工厂保留对其创建的每个对象的引用时,当可以要求创建的对象数量无限时,应用程序最终将耗尽内存。

The flyweight pattern is best suited to scenarios where the number of objects that the factory will need to create and remember is finite and compatible with the memory constraints on the application. That is to say that the application could not be manipulated into extensively creating and remembering objects until it crashes.

flyweight模式最适合工厂需要创建和记住的对象数量有限且与应用程序的内存限制兼容的场景。 这就是说,应用程序只有在崩溃后才能被操纵以广泛地创建和记住对象。

用权重枚举 (Enumeration with Flyweights)

Memory optimization, however, is not the only reason that you would choose to implement the flyweight pattern. The flyweight pattern can also be useful for creating enumeration objects. The Doctrine DBAL is an example of a library that does this. It helps developers to write platform independent code that will work seamlessly with many different storage layers.

但是,内存优化并不是您选择实现flyweight模式的唯一原因。 flyweight模式对于创建枚举对象也很有用。 原则DBAL是执行此操作的库的示例。 它可以帮助开发人员编写独立于平台的代码,这些代码可以与许多不同的存储层无缝协作。

In this library, ‘Type’ objects are used to convert back and forth between database values and PHP values. There are different ‘Type’ objects for strings, integers, booleans, floats, arrays, dates and more.

在此库中,“类型”对象用于在数据库值和PHP值之间来回转换。 对于字符串,整数,布尔值,浮点数,数组,日期等,有不同的“类型”对象。

Here is a very simplified version of the abstract ‘Type’ class from the Doctrine DBAL:

这是来自Doctrine DBAL的抽象“ Type”类的非常简化的版本:

abstract class Type { const INTEGER = 'integer'; const STRING = 'string'; const DATETIME = 'datetime'; private static $_typeObjects = array(); private static $_typesMap = array( self::INTEGER => 'Doctrine\DBAL\Types\IntegerType', self::STRING => 'Doctrine\DBAL\Types\StringType', self::DATETIME => 'Doctrine\DBAL\Types\DateTimeType', ); public static function getType($name) { if (!isset(self::$_typeObjects[$name])) { if (!isset(self::$_typesMap[$name])) { throw DBALException::unknownColumnType($name); } self::$_typeObjects[$name] = new self::$_typesMap[$name](); } return self::$_typeObjects[$name]; } // ... }

As you can see, the flyweight pattern is used to enforce that only one object for each type is created. If developers need to obtain a ‘Type’ object, they can call the ‘getType’ method statically, like this:

如您所见,flyweight模式用于强制每种类型只创建一个对象。 如果开发人员需要获取“类型”对象,则可以静态调用“ getType”方法,如下所示:

$integerType = Type::getType(Type::INTEGER);

Using the flyweight pattern in this manner does help to reduce the memory footprint of the library, but it also helps in other ways too.

以这种方式使用flyweight模式确实有助于减少库的内存占用,但是在其他方面也有帮助。

Enumeration objects make more sense when the flyweight pattern is used. Without it, strange things happen. Take this for example:

使用flyweight模式时,枚举对象更有意义。 没有它,奇怪的事情就会发生。 以这个为例:

$type1 = Type::getType(Type::INTEGER); $type2 = Type::getType(Type::INTEGER); if ($type1 === $type2) { echo 'Yay, you used the flyweight pattern!'.PHP_EOL; } else { echo 'Well this is confusing :('.PHP_EOL; }

Without the flyweight pattern, the above test will not pass. This can be particularly confusing to new, and even experienced developers, on a long day!

没有飞重模式,上述测试将无法通过。 在漫长的一天中,这可能会使新的甚至是经验丰富的开发人员感到困惑!

Another example of enumeration objects using the flyweight pattern is php-enum by Marc Bennewitz.

使用flyweight模式的枚举对象的另一个示例是Marc Bennewitz的php-enum 。

摘要 (Summary)

The flyweight pattern is most beneficial in applications where sharing objects can significantly reduce memory use. This pattern is certainly not one that we will commonly encounter in PHP applications, however there are scenarios where it can be useful. Although the pattern is designed to reduce memory use, used incorrectly, memory leaks can occur. Enumeration objects tend to make more sense when the flyweight pattern is used, as there is only ever one instance of an object of each value.

在共享对象可以显着减少内存使用的应用程序中,flyweight模式最有利。 当然,这种模式不是我们在PHP应用程序中经常遇到的模式,但是在某些情况下它可能会有用。 尽管该模式旨在减少内存使用,但使用不当会导致内存泄漏。 使用flyweight模式时,枚举对象往往更有意义,因为每个值只有一个对象实例。

翻译自: https://www.sitepoint.com/flyweight-design-pattern-immutability-perfect-match/

flyweight模式

最新回复(0)