python 实现工厂模式_工厂方法模式及其在Python中的实现

python 实现工厂模式

This article explores the Factory Method design pattern and its implementation in Python. Design patterns became a popular topic in late 90s after the so-called Gang of Four (GoF: Gamma, Helm, Johson, and Vlissides) published their book Design Patterns: Elements of Reusable Object-Oriented Software.

本文探讨了工厂方法设计模式及其在Python中的实现。 在所谓的“四人帮”(GOF:Gamma,Helm,Johson和Vlissides)出版了他们的书《 设计模式:可重用的面向对象软件的元素》之后,设计模式在90年代后期成为一个热门话题。

The book describes design patterns as a core design solution to reoccurring problems in software and classifies each design pattern into categories according to the nature of the problem. Each pattern is given a name, a problem description, a design solution, and an explanation of the consequences of using it.

该书将设计模式描述为解决软件中重复出现的问题的核心设计解决方案,并根据问题的性质将每种设计模式分类 。 每个模式都有一个名称,一个问题描述,一个设计解决方案以及对使用它的后果的解释。

The GoF book describes Factory Method as a creational design pattern. Creational design patterns are related to the creation of objects, and Factory Method is a design pattern that creates objects with a common interface.

GoF书将Factory Method描述为一种创新设计模式。 创建设计模式与对象的创建有关,而“工厂方法”是一种使用公共接口创建对象的设计模式。

This is a recurrent problem that makes Factory Method one of the most widely used design patterns, and it’s very important to understand it and know how apply it.

这是一个经常出现的问题, 使“工厂方法”成为最广泛使用的设计模式之一 ,了解它并知道如何应用它非常重要。

By the end of this article, you will:

在本文末尾,您将

  • Understand the components of Factory Method
  • Recognize opportunities to use Factory Method in your applications
  • Learn to modify existing code and improve its design by using the pattern
  • Learn to identify opportunities where Factory Method is the appropriate design pattern
  • Choose an appropriate implementation of Factory Method
  • Know how to implement a reusable, general purpose solution of Factory Method
  • 了解工厂方法的组成部分
  • 识别在您的应用程序中使用工厂方法的机会
  • 学习使用模式修改现有代码并改善其设计
  • 学习在“工厂方法”是合适的设计模式的地方识别机会
  • 选择适当的工厂方法实施
  • 知道如何实现工厂方法的可重用通用解决方案

Free Bonus: 5 Thoughts On Python Mastery, a free course for Python developers that shows you the roadmap and the mindset you’ll need to take your Python skills to the next level.

免费奖金: 关于Python精通的5个想法 ,这是针对Python开发人员的免费课程,向您展示了将Python技能提升到新水平所需的路线图和心态。

引入工厂方法 (Introducing Factory Method)

Factory Method is a creational design pattern used to create concrete implementations of a common interface.

Factory Method是一种创新的设计模式,用于创建通用接口的具体实现。

It separates the process of creating an object from the code that depends on the interface of the object.

它将创建对象的过程与取决于对象接口的代码分开。

For example, an application requires an object with a specific interface to perform its tasks. The concrete implementation of the interface is identified by some parameter.

例如,应用程序需要具有特定接口的对象来执行其任务。 接口的具体实现由某些参数标识。

Instead of using a complex if/elif/else conditional structure to determine the concrete implementation, the application delegates that decision to a separate component that creates the concrete object. With this approach, the application code is simplified, making it more reusable and easier to maintain.

应用程序没有使用复杂的if/elif/else条件结构来确定具体的实现,而是将该决策委托给创建具体对象的单独组件。 使用这种方法,可以简化应用程序代码,使其更可重用且更易于维护。

Imagine an application that needs to convert a Song object into its string representation using a specified format. Converting an object to a different representation is often called serializing. You’ll often see these requirements implemented in a single function or method that contains all the logic and implementation, like in the following code:

想象一个应用程序需要使用指定的格式将Song对象转换为其string表示形式。 将对象转换为其他表示形式通常称为序列化。 您经常会看到这些要求在包含所有逻辑和实现的单个函数或方法中实现,如以下代码所示:

 # In serializer_demo.py

# In serializer_demo.py

import import json
json
import import xml.etree.ElementTree xml.etree.ElementTree as as et

et

class class SongSong :
    :
    def def __init____init__ (( selfself , , song_idsong_id , , titletitle , , artistartist ):
        ):
        selfself .. song_id song_id = = song_id
        song_id
        selfself .. title title = = title
        title
        selfself .. artist artist = = artist


artist


class class SongSerializerSongSerializer :
    :
    def def serializeserialize (( selfself , , songsong , , formatformat ):
        ):
        if if format format == == 'JSON''JSON' :
            :
            song_info song_info = = {
                {
                'id''id' : : songsong .. song_idsong_id ,
                ,
                'title''title' : : songsong .. titletitle ,
                ,
                'artist''artist' : : songsong .. artist
            artist
            }
            }
            return return jsonjson .. dumpsdumps (( payloadpayload )
        )
        elif elif format format == == 'XML''XML' :
            :
            song_info song_info = = etet .. ElementElement (( 'song''song' , , attribattrib == {{ 'id''id' : : songsong .. song_idsong_id })
            })
            title title = = etet .. SubElementSubElement (( song_infosong_info , , 'title''title' )
            )
            titletitle .. text text = = songsong .. title
            title
            artist artist = = etet .. SubElementSubElement (( song_infosong_info , , 'artist''artist' )
            )
            artistartist .. text text = = songsong .. artist
            artist
            return return etet .. tostringtostring (( song_infosong_info , , encodingencoding == 'unicode''unicode' )
        )
        elseelse :
            :
            raise raise ValueErrorValueError (( formatformat )
)

In the example above, you have a basic Song class to represent a song and a SongSerializer class that can convert a song object into its string representation according to the value of the format parameter.

在上面的示例中,您有一个基本的Song类来表示一首歌曲,还有一个SongSerializer类,可以根据format参数的值将song对象转换为其string表示format

The .serialize() method supports two different formats: JSON and XML. Any other format specified is not supported, so a ValueError exception is raised.

.serialize()方法支持两种不同的格式: JSON和XML 。 不支持指定的任何其他format ,因此引发ValueError异常。

Let’s use the Python interactive shell to see how the code works:

让我们使用Python交互式外壳来查看代码的工作方式:

>>>
>>>
 >>>  import serializer_demo as sd
>>>  song = sd . Song ( '1' , 'Water of Love' , 'Dire Straits' )
>>>  serializer = sd . SongSerializer ()

>>>  serializer . serialize ( song , 'JSON' )
'{"id": "1", "title": "Water of Love", "artist": "Dire Straits"}'

>>>  serializer . serialize ( song , 'XML' )
'Water of LoveDire Straits'

>>>  serializer . serialize ( song , 'YAML' )
Traceback (most recent call last):
  File "" , line 1 , in 
  File "./serializer_demo.py" , line 30 , in serialize
    raise ValueError ( format )
ValueError : YAML

You create a song object and a serializer, and you convert the song to its string representation by using the .serialize() method. The method takes the song object as a parameter, as well as a string value representing the format you want. The last call uses YAML as the format, which is not supported by the serializer, so a ValueError exception is raised.

您创建一个song对象和一个serializer ,然后使用.serialize()方法将歌曲转换为其字符串表示形式。 该方法将song对象作为参数,以及代表所需格式的字符串值。 最后一次调用使用YAML作为格式,而serializer器不支持该格式,因此引发ValueError异常。

This example is short and simplified, but it still has a lot of complexity. There are three logical or execution paths depending on the value of the format parameter. This may not seem like a big deal, and you’ve probably seen code with more complexity than this, but the above example is still pretty hard to maintain.

这个例子简短而简化,但是仍然很复杂。 根据format参数的值,共有三个逻辑或执行路径。 这看起来似乎没什么大不了的,您可能已经看到了比这复杂的代码,但是上面的示例仍然很难维护。

复杂条件代码的问题 (The Problems With Complex Conditional Code)

The example above exhibits all the problems you’ll find in complex logical code. Complex logical code uses if/elif/else structures to change the behavior of an application. Using if/elif/else conditional structures makes the code harder to read, harder to understand, and harder to maintain.

上面的示例展示了您将在复杂的逻辑代码中发现的所有问题。 复杂的逻辑代码使用if/elif/else结构来更改应用程序的行为。 使用if/elif/else条件结构会使代码更难阅读,更难理解且难以维护。

The code above might not seem hard to read or understand, but wait till you see the final code in this section!

上面的代码似乎很难阅读或理解,但是请等到您看到本节中的最终代码!

Nevertheless, the code above is hard to maintain because it is doing too much. The single responsibility principle states that a module, a class, or even a method should have a single, well-defined responsibility. It should do just one thing and have only one reason to change.

但是,上面的代码很难维护,因为它做得太多。 单一责任原则指出,模块,类甚至方法应具有单一的,明确定义的责任。 它应该只做一件事,并且只有一个改变的理由。

The .serialize() method in SongSerializer will require changes for many different reasons. This increases the risk of introducing new defects or breaking existing functionality when changes are made. Let’s take a look at all the situations that will require modifications to the implementation:

.serialize()的方法SongSerializer将需要许多不同的原因的变化。 进行更改时,这会增加引入新缺陷或破坏现有功能的风险。 让我们看一下所有需要修改实现的情况:

  • When a new format is introduced: The method will have to change to implement the serialization to that format.

  • When the Song object changes: Adding or removing properties to the Song class will require the implementation to change in order to accommodate the new structure.

  • When the string representation for a format changes (plain JSON vs JSON API): The .serialize() method will have to change if the desired string representation for a format changes because the representation is hard-coded in the .serialize() method implementation.

  • 引入新格式时:必须更改方法以实现对该格式的序列化。

  • Song对象更改时:Song类添加或删除属性将需要更改实现以适应新结构。

  • 当格式的字符串表示形式更改时(纯JSON与JSON API相比 ):如果更改所需的格式的字符串表示形式,则.serialize()方法将必须更改,因为该表示形式在.serialize()方法实现中进行了硬编码。

The ideal situation would be if any of those changes in requirements could be implemented without changing the .serialize() method. Let’s see how you can do that in the following sections.

理想的情况是是否可以在不更改.serialize()方法的情况下实现需求的任何更改。 接下来的部分让我们看看如何做到这一点。

寻找通用界面 (Looking for a Common Interface)

The first step when you see complex conditional code in an application is to identify the common goal of each of the execution paths (or logical paths).

当您在应用程序中看到复杂的条件代码时,第一步是确定每个执行路径(或逻辑路径)的共同目标。

Code that uses if/elif/else usually has a common goal that is implemented in different ways in each logical path. The code above converts a song object to its string representation using a different format in each logical path.

使用if/elif/else通常具有一个共同的目标,该目标在每个逻辑路径中以不同的方式实现。 上面的代码在每个逻辑路径中使用不同的格式将song对象转换为其string表示形式。

Based on the goal, you look for a common interface that can be used to replace each of the paths. The example above requires an interface that takes a song object and returns a string.

根据目标,您将寻找一个可用于替换每个路径的通用接口。 上面的示例需要一个接收song对象并返回string的接口。

Once you have a common interface, you provide separate implementations for each logical path. In the example above, you will provide an implementation to serialize to JSON and another for XML.

一旦有了通用接口,就可以为每个逻辑路径提供单独的实现。 在上面的示例中,您将提供一个实现以序列化为JSON,另一个实现为XML。

Then, you provide a separate component that decides the concrete implementation to use based on the specified format. This component evaluates the value of format and returns the concrete implementation identified by its value.

然后,您提供一个单独的组件,该组件根据指定的format决定要使用的具体实现。 该组件评估format的值,并返回由其值标识的具体实现。

In the following sections, you will learn how to make changes to existing code without changing the behavior. This is referred to as refactoring the code.

在以下各节中,您将学习如何在不更改行为的情况下更改现有代码。 这称为重构代码。

Martin Fowler in his book Refactoring: Improving the Design of Existing Code defines refactoring as “the process of changing a software system in such a way that does not alter the external behavior of the code yet improves its internal structure.”

马丁·福勒(Martin Fowler)在他的《 重构:改善现有代码的设计》一书中将重构定义为“以不改变代码的外部行为但改善其内部结构的方式更改软件系统的过程”。

Let’s begin refactoring the code to achieve the desired structure that uses the Factory Method design pattern.

让我们开始重构代码,以实现使用Factory Method设计模式的所需结构。

将代码重构为所需的接口 (Refactoring Code Into the Desired Interface)

The desired interface is an object or a function that takes a Song object and returns a string representation.

所需的接口是接受Song对象并返回string表示形式的对象或函数。

The first step is to refactor one of the logical paths into this interface. You do this by adding a new method ._serialize_to_json() and moving the JSON serialization code to it. Then, you change the client to call it instead of having the implementation in the body of the if statement:

第一步是将逻辑路径之一重构到此接口中。 您可以通过添加新方法._serialize_to_json()并将JSON序列化代码移至其中来完成此操作。 然后,将客户端更改为调用它,而不是在if语句的主体中包含实现:

Once you make this change, you can verify that the behavior has not changed. Then, you do the same for the XML option by introducing a new method ._serialize_to_xml(), moving the implementation to it, and modifying the elif path to call it.

进行更改后,可以验证行为没有更改。 然后,通过引入新方法._serialize_to_xml() ,将实现移至该方法并修改elif路径以对其进行调用,对XML选项执行相同的操作。

The following example shows the refactored code:

以下示例显示了重构的代码:

 class class SongSerializerSongSerializer :
    :
    def def serializeserialize (( selfself , , songsong , , formatformat ):
        ):
        if if format format == == 'JSON''JSON' :
            :
            return return selfself .. _serialize_to_json_serialize_to_json (( songsong )
        )
        elif elif format format == == 'XML''XML' :
            :
            return return selfself .. _serialize_to_xml_serialize_to_xml (( songsong )
        )
        elseelse :
            :
            raise raise ValueErrorValueError (( formatformat )

    )

    def def _serialize_to_json_serialize_to_json (( selfself , , songsong ):
        ):
        payload payload = = {
            {
            'id''id' : : songsong .. song_idsong_id ,
            ,
            'title''title' : : songsong .. titletitle ,
            ,
            'artist''artist' : : songsong .. artist
        artist
        }
        }
        return return jsonjson .. dumpsdumps (( payloadpayload )

    )

    def def _serialize_to_xml_serialize_to_xml (( selfself , , songsong ):
        ):
        song_element song_element = = etet .. ElementElement (( 'song''song' , , attribattrib == {{ 'id''id' : : songsong .. song_idsong_id })
        })
        title title = = etet .. SubElementSubElement (( song_elementsong_element , , 'title''title' )
        )
        titletitle .. text text = = songsong .. title
        title
        artist artist = = etet .. SubElementSubElement (( song_elementsong_element , , 'artist''artist' )
        )
        artistartist .. text text = = songsong .. artist
        artist
        return return etet .. tostringtostring (( song_elementsong_element , , encodingencoding == 'unicode''unicode' )
)

The new version of the code is easier to read and understand, but it can still be improved with a basic implementation of Factory Method.

新版本的代码更易于阅读和理解,但仍可以通过工厂方法的基本实现进行改进。

工厂方法的基本实现 (Basic Implementation of Factory Method)

The central idea in Factory Method is to provide a separate component with the responsibility to decide which concrete implementation should be used based on some specified parameter. That parameter in our example is the format.

Factory Method中的中心思想是提供一个单独的组件,该组件负责根据某些指定的参数来决定应使用哪种具体实现。 在我们的示例中,该参数为format

To complete the implementation of Factory Method, you add a new method ._get_serializer() that takes the desired format. This method evaluates the value of format and returns the matching serialization function:

要完成Factory Method的实现,请添加采用所需format的新方法._get_serializer() 。 此方法评估format的值并返回匹配的序列化函数:

Note: The ._get_serializer() method does not call the concrete implementation, and it just returns the function object itself.

注意: ._get_serializer()方法不会调用具体的实现,而只是返回函数对象本身。

Now, you can change the .serialize() method of SongSerializer to use ._get_serializer() to complete the Factory Method implementation. The next example shows the complete code:

现在,您可以将.serialize()方法SongSerializer为使用._get_serializer()来完成Factory Method实现。 下一个示例显示完整的代码:

 class class SongSerializerSongSerializer :
    :
    def def serializeserialize (( selfself , , songsong , , formatformat ):
        ):
        serializer serializer = = selfself .. _get_serializer_get_serializer (( formatformat )
        )
        return return serializerserializer (( songsong )

    )

    def def _get_serializer_get_serializer (( selfself , , formatformat ):
        ):
        if if format format == == 'JSON''JSON' :
            :
            return return selfself .. _serialize_to_json
        _serialize_to_json
        elif elif format format == == 'XML''XML' :
            :
            return return selfself .. _serialize_to_xml
        _serialize_to_xml
        elseelse :
            :
            raise raise ValueErrorValueError (( formatformat )

    )

    def def _serialize_to_json_serialize_to_json (( selfself , , songsong ):
        ):
        payload payload = = {
            {
            'id''id' : : songsong .. song_idsong_id ,
            ,
            'title''title' : : songsong .. titletitle ,
            ,
            'artist''artist' : : songsong .. artist
        artist
        }
        }
        return return jsonjson .. dumpsdumps (( payloadpayload )

    )

    def def _serialize_to_xml_serialize_to_xml (( selfself , , songsong ):
        ):
        song_element song_element = = etet .. ElementElement (( 'song''song' , , attribattrib == {{ 'id''id' : : songsong .. song_idsong_id })
        })
        title title = = etet .. SubElementSubElement (( song_elementsong_element , , 'title''title' )
        )
        titletitle .. text text = = songsong .. title
        title
        artist artist = = etet .. SubElementSubElement (( song_elementsong_element , , 'artist''artist' )
        )
        artistartist .. text text = = songsong .. artist
        artist
        return return etet .. tostringtostring (( song_elementsong_element , , encodingencoding == 'unicode''unicode' )
)

The final implementation shows the different components of Factory Method. The .serialize() method is the application code that depends on an interface to complete its task.

最终实现显示了工厂方法的不同组成部分。 .serialize()方法是依赖于接口来完成其任务的应用程序代码。

This is referred to as the client component of the pattern. The interface defined is referred to as the product component. In our case, the product is a function that takes a Song and returns a string representation.

这称为模式的客户端组件。 定义的接口称为产品组件。 在我们的例子中,乘积是一个接受Song并返回字符串表示形式的函数。

The ._serialize_to_json() and ._serialize_to_xml() methods are concrete implementations of the product. Finally, the ._get_serializer() method is the creator component. The creator decides which concrete implementation to use.

._serialize_to_json()._serialize_to_xml()方法是产品的具体实现。 最后, ._get_serializer()方法是创建者组件。 创建者决定使用哪种具体实现。

Because you started with some existing code, all the components of Factory Method are members of the same class SongSerializer.

因为您从一些现有代码开始,所以Factory Method的所有组件都是同一类SongSerializer成员。

Usually, this is not the case and, as you can see, none of the added methods use the self parameter. This is a good indication that they should not be methods of the SongSerializer class, and they can become external functions:

通常情况并非如此,如您所见,添加的方法都不使用self参数。 这很好地表明了它们不应该是SongSerializer类的方法,它们可以成为外部函数:

Note: The .serialize() method in SongSerializer does not use the self parameter.

注: .serialize()的方法SongSerializer不使用self的参数。

The rule above tells us it should not be part of the class. This is correct, but you are dealing with existing code.

上面的规则告诉我们,它不应该属于该类。 这是正确的,但是您正在处理现有代码。

If you remove SongSerializer and change the .serialize() method to a function, then you’ll have to change all the locations in the application that use SongSerializer and replace the calls to the new function.

如果删除SongSerializer并将.serialize()方法更改为一个函数,则必须更改应用程序中所有使用SongSerializer的位置,并替换对新函数的调用。

Unless you have a very high percentage of code coverage with your unit tests, this is not a change that you should be doing.

除非单元测试的代码覆盖率很高,否则这不是您应该做的更改。

The mechanics of Factory Method are always the same. A client (SongSerializer.serialize()) depends on a concrete implementation of an interface. It requests the implementation from a creator component (get_serializer()) using some sort of identifier (format).

工厂方法的机制始终相同。 客户端( SongSerializer.serialize() )取决于接口的具体实现。 它使用某种标识符( format )从创建者组件( get_serializer() )请求实现。

The creator returns the concrete implementation according to the value of the parameter to the client, and the client uses the provided object to complete its task.

创建者根据参数的值将具体实现返回给客户端,客户端使用提供的对象完成其任务。

You can execute the same set of instructions in the Python interactive interpreter to verify that the application behavior has not changed:

您可以在Python交互式解释器中执行同一组指令,以验证应用程序的行为没有改变:

>>>
>>> import serializer_demo as sd
>>> song = sd.Song('1', 'Water of Love', 'Dire Straits')
>>> serializer = sd.SongSerializer()

>>> serializer.serialize(song, 'JSON')
'{"id": "1", "title": "Water of Love", "artist": "Dire Straits"}'

>>> serializer.serialize(song, 'XML')
'Water of LoveDire Straits'

>>> serializer.serialize(song, 'YAML')
Traceback (most recent call last):
  File "", line 1, in 
  File "./serializer_demo.py", line 13, in serialize
    serializer = get_serializer(format)
  File "./serializer_demo.py", line 23, in get_serializer
    raise ValueError(format)
ValueError: YAML

>>>

You create a song and a serializer, and use the serializer to convert the song to its string representation specifying a format. Since, YAML is not a supported format, ValueError is raised.

创建一song和一个serializer ,然后使用serializer程序将歌曲转换为指定format string表示format 。 由于YAML不是受支持的格式,因此引发ValueError

认识使用工厂方法的机会 (Recognizing Opportunities to Use Factory Method)

Factory Method should be used in every situation where an application (client) depends on an interface (product) to perform a task and there are multiple concrete implementations of that interface. You need to provide a parameter that can identify the concrete implementation and use it in the creator to decide the concrete implementation.

在应用程序(客户端)依赖于接口(产品)执行任务并且该接口有多个具体实现的每种情况下,都应使用工厂方法。 您需要提供一个可以识别具体实现的参数,并在创建者中使用它来确定具体实现。

There is a wide range of problems that fit this description, so let’s take a look at some concrete examples.

符合此描述的问题很多,因此让我们看一些具体示例。

Replacing complex logical code: Complex logical structures in the format if/elif/else are hard to maintain because new logical paths are needed as requirements change.

替换复杂的逻辑代码:很难维护if/elif/else格式的复杂逻辑结构,因为随着需求的变化需要新的逻辑路径。

Factory Method is a good replacement because you can put the body of each logical path into separate functions or classes with a common interface, and the creator can provide the concrete implementation.

Factory Method是一个很好的替代方法,因为您可以将每个逻辑路径的主体放入具有公共接口的单独的函数或类中,并且创建者可以提供具体的实现。

The parameter evaluated in the conditions becomes the parameter to identify the concrete implementation. The example above represents this situation.

条件中评估的参数将成为识别具体实现的参数。 上面的示例代表了这种情况。

Constructing related objects from external data: Imagine an application that needs to retrieve employee information from a database or other external source.

从外部数据构造相关对象:想象一个需要从数据库或其他外部源检索员工信息的应用程序。

The records represent employees with different roles or types: managers, office clerks, sales associates, and so on. The application may store an identifier representing the type of employee in the record and then use Factory Method to create each concrete Employee object from the rest of the information on the record.

记录代表具有不同角色或类型的员工:经理,办公室文员,销售助理等。 应用程序可以在记录中存储代表员工类型的标识符,然后使用Factory Method从记录中的其余信息创建每个具体的Employee对象。

Supporting multiple implementations of the same feature: An image processing application needs to transform a satellite image from one coordinate system to another, but there are multiple algorithms with different levels of accuracy to perform the transformation.

支持相同功能的多种实现:图像处理应用程序需要将卫星图像从一个坐标系转换到另一个坐标系,但是有多种算法具有不同的精度级别来执行转换。

The application can allow the user to select an option that identifies the concrete algorithm. Factory Method can provide the concrete implementation of the algorithm based on this option.

该应用程序可以允许用户选择一个标识具体算法的选项。 Factory Method可以基于此选项提供算法的具体实现。

Combining similar features under a common interface: Following the image processing example, an application needs to apply a filter to an image. The specific filter to use can be identified by some user input, and Factory Method can provide the concrete filter implementation.

在公共界面下结合相似的功能:在图像处理示例之后,应用程序需要对图像应用滤镜。 可以通过一些用户输入来标识要使用的特定过滤器,并且Factory Method可以提供具体的过滤器实现。

Integrating related external services: A music player application wants to integrate with multiple external services and allow users to select where their music comes from. The application can define a common interface for a music service and use Factory Method to create the correct integration based on a user preference.

集成相关的外部服务:音乐播放器应用程序希望与多个外部服务集成,并允许用户选择其音乐来源。 该应用程序可以为音乐服务定义一个通用接口,并使用Factory Method根据用户的喜好创建正确的集成。

All these situations are similar. They all define a client that depends on a common interface known as the product. They all provide a means to identify the concrete implementation of the product, so they all can use Factory Method in their design.

所有这些情况都是相似的。 它们都定义了一个客户端,该客户端依赖于称为产品的通用接口。 它们都提供了一种方法来标识产品的具体实现,因此它们都可以在设计中使用工厂方法。

You can now look at the serialization problem from previous examples and provide a better design by taking into consideration the Factory Method design pattern.

现在,您可以从前面的示例中查看序列化问题,并通过考虑Factory Method设计模式来提供更好的设计。

对象序列化示例 (An Object Serialization Example)

The basic requirements for the example above are that you want to serialize Song objects into their string representation. It seems the application provides features related to music, so it is plausible that the application will need to serialize other type of objects like Playlist or Album.

上面示例的基本要求是您希望将Song对象序列化为其string表示形式。 似乎该应用程序提供了与音乐相关的功能,因此该应用程序需要序列化其他类型的对象(如PlaylistAlbum )似乎是合理的。

Ideally, the design should support adding serialization for new objects by implementing new classes without requiring changes to the existing implementation. The application requires objects to be serialized to multiple formats like JSON and XML, so it seems natural to define an interface Serializer that can have multiple implementations, one per format.

理想情况下,设计应支持通过实现新类而为新对象添加序列化,而无需更改现有实现。 该应用程序要求将对象序列化为JSON和XML等多种格式,因此定义一个接口Serializer似乎很自然,该接口可以具有多种实现,每种格式一个。

The interface implementation might look something like this:

接口实现可能看起来像这样:

 # In serializers.py

# In serializers.py

import import json
json
import import xml.etree.ElementTree xml.etree.ElementTree as as et 

et 

class class JsonSerializerJsonSerializer :
    :
    def def __init____init__ (( selfself ):
        ):
        selfself .. _current_object _current_object = = None

    None

    def def start_objectstart_object (( selfself , , object_nameobject_name , , object_idobject_id ):
        ):
        selfself .. _current_object _current_object = = {
            {
            'id''id' : : object_id
        object_id
        }

    }

    def def add_propertyadd_property (( selfself , , namename , , valuevalue ):
        ):
        selfself .. _current_object_current_object [[ namename ] ] = = value

    value

    def def to_strto_str (( selfself ):
        ):
        return return jsonjson .. dumpsdumps (( selfself .. _current_object_current_object )


)


class class XmlSerializerXmlSerializer :
    :
    def def __init____init__ (( selfself ):
        ):
        selfself .. _element _element = = None

    None

    def def start_objectstart_object (( selfself , , object_nameobject_name , , object_idobject_id ):
        ):
        selfself .. _element _element = = etet .. ElementElement (( object_nameobject_name , , attribattrib == {{ 'id''id' : : object_idobject_id })

    })

    def def add_propertyadd_property (( selfself , , namename , , valuevalue ):
        ):
        prop prop = = etet .. SubElementSubElement (( selfself .. _element_element , , namename )
        )
        propprop .. text text = = value

    value

    def def to_strto_str (( selfself ):
        ):
        return return etet .. tostringtostring (( selfself .. _element_element , , encodingencoding == 'unicode''unicode' )
)

Note: The example above doesn’t implement a full Serializer interface, but it should be good enough for our purposes and to demonstrate Factory Method.

注意:上面的示例未实现完整的Serializer接口,但对于我们的目的和演示Factory Method应该足够好。

The Serializer interface is an abstract concept due to the dynamic nature of the Python language. Static languages like Java or C# require that interfaces be explicitly defined. In Python, any object that provides the desired methods or functions is said to implement the interface. The example defines the Serializer interface to be an object that implements the following methods or functions:

由于Python语言的动态特性, Serializer接口是一个抽象概念。 诸如Java或C#之类的静态语言要求明确定义接口。 在Python中,任何提供所需方法或功能的对象都称为实现接口。 该示例将Serializer接口定义为实现以下方法或功能的对象:

  • .start_object(object_name, object_id)
  • .add_property(name, value)
  • .to_str()
  • .start_object(object_name, object_id)
  • .add_property(name, value)
  • .to_str()

This interface is implemented by the concrete classes JsonSerializer and XmlSerializer.

该接口由具体的类JsonSerializerXmlSerializer

The original example used a SongSerializer class. For the new application, you will implement something more generic, like ObjectSerializer:

原始示例使用了SongSerializer类。 对于新应用程序,您将实现一些更通用的东西,例如ObjectSerializer

The implementation of ObjectSerializer is completely generic, and it only mentions a serializable and a format as parameters.

ObjectSerializer的实现是完全通用的,并且仅提及可serializableformatformat作为参数。

The format is used to identify the concrete implementation of the Serializer and is resolved by the factory object. The serializable parameter refers to another abstract interface that should be implemented on any object type you want to serialize.

format用于标识Serializer的具体实现,并由factory对象解析。 serializable参数是另一个抽象接口,应该在要序列化的任何对象类型上实现。

Let’s take a look at a concrete implementation of the serializable interface in the Song class:

让我们看一下Song类中可serializable接口的具体实现:

 # In songs.py

# In songs.py

class class SongSong :
    :
    def def __init____init__ (( selfself , , song_idsong_id , , titletitle , , artistartist ):
        ):
        selfself .. song_id song_id = = song_id
        song_id
        selfself .. title title = = title
        title
        selfself .. artist artist = = artist

    artist

    def def serializeserialize (( selfself , , serializerserializer ):
        ):
        serializerserializer .. start_objectstart_object (( 'song''song' , , selfself .. song_idsong_id )
        )
        serializerserializer .. add_propertyadd_property (( 'title''title' , , selfself .. titletitle )
        )
        serializerserializer .. add_propertyadd_property (( 'artist''artist' , , selfself .. artistartist )
)

The Song class implements the Serializable interface by providing a .serialize(serializer) method. In the method, the Song class uses the serializer object to write its own information without any knowledge of the format.

Song类通过提供.serialize(serializer)方法来实现Serializable接口。 在该方法中, Song类使用serializer对象编写自己的信息,而无需任何格式的知识。

As a matter of fact, the Song class doesn’t even know the goal is to convert the data to a string. This is important because you could use this interface to provide a different kind of serializer that converts the Song information to a completely different representation if needed. For example, your application might require in the future to convert the Song object to a binary format.

事实上, Song类甚至不知道目标是将数据转换为字符串。 这很重要,因为您可以使用此接口来提供另一种类型的serializer ,如果需要,该serializer程序会将Song信息转换为完全不同的表示形式。 例如,您的应用程序将来可能需要将Song对象转换为二进制格式。

So far, we’ve seen the implementation of the client (ObjectSerializer) and the product (serializer). It is time to complete the implementation of Factory Method and provide the creator. The creator in the example is the variable factory in ObjectSerializer.serialize().

到目前为止,我们已经看到了客户端( ObjectSerializer )和产品( serializer )的实现。 现在该完成Factory Method的实现并提供创建者了。 该示例中的创建者是ObjectSerializer.serialize()的变量factory

工厂方法作为对象工厂 (Factory Method as an Object Factory)

In the original example, you implemented the creator as a function. Functions are fine for very simple examples, but they don’t provide too much flexibility when requirements change.

在原始示例中,您将创建者实现为一个函数。 函数对于非常简单的示例来说很好,但是当需求改变时,它们并不能提供太多的灵活性。

Classes can provide additional interfaces to add functionality, and they can be derived to customize behavior. Unless you have a very basic creator that will never change in the future, you want to implement it as a class and not a function. These type of classes are called object factories.

类可以提供其他接口来添加功能,并且可以派生它们来定制行为。 除非您有一个非常基本的创建者,以后再也不会改变,否则您希望将其实现为类而不是函数。 这些类型的类称为对象工厂。

You can see the basic interface of SerializerFactory in the implementation of ObjectSerializer.serialize(). The method uses factory.get_serializer(format) to retrieve the serializer from the object factory.

您可以在ObjectSerializer.serialize()的实现中看到SerializerFactory的基本接口。 该方法使用factory.get_serializer(format)从对象工厂检索serializer

You will now implement SerializerFactory to meet this interface:

现在,您将实现SerializerFactory以满足以下接口:

The current implementation of .get_serializer() is the same you used in the original example. The method evaluates the value of format and decides the concrete implementation to create and return. It is a relatively simple solution that allows us to verify the functionality of all the Factory Method components.

.get_serializer()的当前实现与原始示例中使用的相同。 该方法评估format的值,并确定要创建和返回的具体实现。 这是一个相对简单的解决方案,可让我们验证所有“工厂方法”组件的功能。

Let’s go to the Python interactive interpreter and see how it works:

让我们转到Python交互式解释器,看看它是如何工作的:

>>>
>>> import songs
>>> import serializers
>>> song = songs.Song('1', 'Water of Love', 'Dire Straits')
>>> serializer = serializers.ObjectSerializer()

>>> serializer.serialize(song, 'JSON')
'{"id": "1", "title": "Water of Love", "artist": "Dire Straits"}'

>>> serializer.serialize(song, 'XML')
'Water of LoveDire Straits'

>>> serializer.serialize(song, 'YAML')
Traceback (most recent call last):
  File "", line 1, in 
  File "./serializers.py", line 39, in serialize
    serializer = factory.get_serializer(format)
  File "./serializers.py", line 52, in get_serializer
    raise ValueError(format)
ValueError: YAML

>>>

The new design of Factory Method allows the application to introduce new features by adding new classes, as opposed to changing existing ones. You can serialize other objects by implementing the Serializable interface on them. You can support new formats by implementing the Serializer interface in another class.

工厂方法的新设计允许应用程序通过添加新类来引入新功能,而不是更改现有功能。 您可以通过在其他对象上实现Serializable接口来序列化其他对象。 您可以通过在另一个类中实现Serializer接口来支持新格式。

The missing piece is that SerializerFactory has to change to include the support for new formats. This problem is easily solved with the new design because SerializerFactory is a class.

缺少的部分是SerializerFactory必须更改为包括对新格式的支持。 使用新设计可以轻松解决此问题,因为SerializerFactory是一个类。

支持其他格式 (Supporting Additional Formats)

The current implementation of SerializerFactory needs to be changed when a new format is introduced. Your application might never need to support any additional formats, but you never know.

引入新格式后,需要更改SerializerFactory的当前实现。 您的应用程序可能永远不需要支持任何其他格式,但是您永远不会知道。

You want your designs to be flexible, and as you will see, supporting additional formats without changing SerializerFactory is relatively easy.

您希望您的设计灵活,并且如您所见,在不更改SerializerFactory情况下支持其他格式相对容易。

The idea is to provide a method in SerializerFactory that registers a new Serializer implementation for the format we want to support:

这个想法是在SerializerFactory中提供一种方法,该方法针对我们要支持的格式注册一个新的Serializer实现:

 # In serializers.py

# In serializers.py

class class SerializerFactorySerializerFactory :

    :

    def def __init____init__ (( selfself ):
        ):
        selfself .. _creators _creators = = {}

    {}

    def def register_formatregister_format (( selfself , , formatformat , , creatorcreator ):
        ):
        selfself .. _creators_creators [[ formatformat ] ] = = creator

    creator

    def def get_serializerget_serializer (( selfself , , formatformat ):
        ):
        creator creator = = selfself .. _creators_creators .. getget (( formatformat )
        )
        if if not not creatorcreator :
            :
            raise raise ValueErrorValueError (( formatformat )
        )
        return return creatorcreator ()


()


factory factory = = SerializerFactorySerializerFactory ()
()
factoryfactory .. register_formatregister_format (( 'JSON''JSON' , , JsonSerializerJsonSerializer )
)
factoryfactory .. register_formatregister_format (( 'XML''XML' , , XmlSerializerXmlSerializer )
)

The .register_format(format, creator) method allows registering new formats by specifying a format value used to identify the format and a creator object. The creator object happens to be the class name of the concrete Serializer. This is possible because all the Serializer classes provide a default .__init__() to initialize the instances.

.register_format(format, creator)方法允许通过指定用于标识格式的format值和creator对象来注册新格式。 创建者对象恰好是具体Serializer的类名。 这是可能的,因为所有Serializer类都提供默认的.__init__()来初始化实例。

The registration information is stored in the _creators dictionary. The .get_serializer() method retrieves the registered creator and creates the desired object. If the requested format has not been registered, then ValueError is raised.

注册信息存储在_creators词典中。 .get_serializer()方法检索注册的创建者并创建所需的对象。 如果请求的format尚未注册,则引发ValueError

You can now verify the flexibility of the design by implementing a YamlSerializer and get rid of the annoying ValueError you saw earlier:

现在,您可以通过实现YamlSerializer来验证设计的灵活性,并摆脱先前看到的烦人的ValueError

Note: To implement the example, you need to install PyYAML in your environment using pip install PyYAML.

注:要实现的例子,您需要安装PyYAML使用环境pip install PyYAML

JSON and YAML are very similar formats, so you can reuse most of the implementation of JsonSerializer and overwrite .to_str() to complete the implementation. The format is then registered with the factory object to make it available.

JSON和YAML是非常相似的格式,因此您可以重用JsonSerializer大部分实现并覆盖.to_str()以完成实现。 然后将该格式注册到factory对象中以使其可用。

Let’s use the Python interactive interpreter to see the results:

让我们使用Python交互式解释器查看结果:

>>>
>>> import serializers
>>> import songs
>>> import yaml_serializer
>>> song = songs.Song('1', 'Water of Love', 'Dire Straits')
>>> serializer = serializers.ObjectSerializer()

>>> print(serializer.serialize(song, 'JSON'))
{"id": "1", "title": "Water of Love", "artist": "Dire Straits"}

>>> print(serializer.serialize(song, 'XML'))
Water of LoveDire Straits

>>> print(serializer.serialize(song, 'YAML'))
{artist: Dire Straits, id: '1', title: Water of Love}

>>>

By implementing Factory Method using an Object Factory and providing a registration interface, you are able to support new formats without changing any of the existing application code. This minimizes the risk of breaking existing features or introducing subtle bugs.

通过使用对象工厂实现工厂方法并提供注册接口,您可以支持新格式,而无需更改任何现有应用程序代码。 这样可以最大程度地降低破坏现有功能或引入细微错误的风险。

通用对象工厂 (A General Purpose Object Factory)

The implementation of SerializerFactory is a huge improvement from the original example. It provides great flexibility to support new formats and avoids modifying existing code.

SerializerFactory的实现是对原始示例的巨大改进。 它为支持新格式提供了极大的灵活性,并且避免了修改现有代码的麻烦。

Still, the current implementation is specifically targeted to the serialization problem above, and it is not reusable in other contexts.

尽管如此,当前的实现仍然专门针对上述序列化问题,并且在其他情况下不可重用。

Factory Method can be used to solve a wide range of problems. An Object Factory gives additional flexibility to the design when requirements change. Ideally, you’ll want an implementation of Object Factory that can be reused in any situation without replicating the implementation.

工厂方法可用于解决各种问题。 当需求改变时,对象工厂为设计提供了额外的灵活性。 理想情况下,您需要一个Object Factory的实现,该实现可以在任何情况下都可以重用,而无需复制该实现。

There are some challenges to providing a general purpose implementation of Object Factory, and in the following sections you will look at those challenges and implement a solution that can be reused in any situation.

提供对象工厂的通用实现存在一些挑战,在以下部分中,您将研究这些挑战并实现可在任何情况下重用的解决方案。

并非所有对象都可以相等地创建 (Not All Objects Can Be Created Equal)

The biggest challenge to implement a general purpose Object Factory is that not all objects are created in the same way.

实现通用对象工厂的最大挑战是,并非所有对象都以相同的方式创建。

Not all situations allow us to use a default .__init__() to create and initialize the objects. It is important that the creator, in this case the Object Factory, returns fully initialized objects.

并非所有情况都允许我们使用默认的.__init__()创建和初始化对象。 创建者(在这种情况下为对象工厂)返回完全初始化的对象非常重要。

This is important because if it doesn’t, then the client will have to complete the initialization and use complex conditional code to fully initialize the provided objects. This defeats the purpose of the Factory Method design pattern.

这很重要,因为如果不这样做,则客户端将必须完成初始化并使用复杂的条件代码来完全初始化所提供的对象。 这违反了“工厂方法”设计模式的目的。

To understand the complexities of a general purpose solution, let’s take a look at a different problem. Let’s say an application wants to integrate with different music services. These services can be external to the application or internal in order to support a local music collection. Each of the services has a different set of requirements.

为了了解通用解决方案的复杂性,让我们看一个不同的问题。 假设某个应用程序想要与不同的音乐服务集成。 这些服务可以在应用程序外部或内部,以支持本地音乐收藏。 每个服务都有一组不同的要求。

Note: The requirements I define for the example are for illustration purposes and do not reflect the real requirements you will have to implement to integrate with services like Pandora or Spotify.

注意:我为示例定义的需求仅用于说明目的,并不反映与Pandora或Spotify之类的服务集成时必须实现的实际需求。

The intent is to provide a different set of requirements that shows the challenges of implementing a general purpose Object Factory.

目的是提供一组不同的要求,以显示实现通用对象工厂的挑战。

Imagine that the application wants to integrate with a service provided by Spotify. This service requires an authorization process where a client key and secret are provided for authorization.

想象一下,该应用程序想要与Spotify提供的服务集成。 此服务需要一个授权过程,其中提供了用于授权的客户端密钥和机密。

The service returns an access code that should be used on any further communication. This authorization process is very slow, and it should only be performed once, so the application wants to keep the initialized service object around and use it every time it needs to communicate with Spotify.

该服务返回一个访问代码,该代码应在任何进一步的通信中使用。 该授权过程非常缓慢,并且只能执行一次,因此应用程序希望保留初始化的服务对象,并在每次需要与Spotify通信时使用它。

At the same time, other users want to integrate with Pandora. Pandora might use a completely different authorization process. It also requires a client key and secret, but it returns a consumer key and secret that should be used for other communications. As with Spotify, the authorization process is slow, and it should only be performed once.

同时,其他用户希望与Pandora集成。 Pandora可能使用完全不同的授权过程。 它还需要一个客户端密钥和机密,但是它返回应该用于其他通信的消费者密钥和机密。 与Spotify一样,授权过程很慢,应该只执行一次。

Finally, the application implements the concept of a local music service where the music collection is stored locally. The service requires that the the location of the music collection in the local system be specified. Creating a new service instance is done very quickly, so a new instance can be created every time the user wants to access the music collection.

最后,该应用程序实现了本地音乐服务的概念,其中音乐收藏存储在本地。 该服务要求指定音乐收藏在本地系统中的位置。 创建新服务实例的过程非常快,因此每次用户想要访问音乐收藏时都可以创建一个新实例。

This example presents several challenges. Each service is initialized with a different set of parameters. Also, Spotify and Pandora require an authorization process before the service instance can be created.

这个例子提出了几个挑战。 每个服务都使用一组不同的参数初始化。 另外,在创建服务实例之前,Spotify和Pandora需要进行授权过程。

They also want to reuse that instance to avoid authorizing the application multiple times. The local service is simpler, but it doesn’t match the initialization interface of the others.

他们还希望重用该实例,以避免多次授权应用程序。 本地服务比较简单,但是与其他服务的初始化接口不匹配。

In the following sections, you will solve this problems by generalizing the creation interface and implementing a general purpose Object Factory.

在以下各节中,您将通过概括创建接口并实现通用的Object Factory来解决此问题。

单独的对象创建以提供通用接口 (Separate Object Creation to Provide Common Interface)

The creation of each concrete music service has its own set of requirements. This means a common initialization interface for each service implementation is not possible or recommended.

每个具体音乐服务的创建都有其自己的一组要求。 这意味着不可能或不建议为每个服务实现使用通用的初始化接口。

The best approach is to define a new type of object that provides a general interface and is responsible for the creation of a concrete service. This new type of object will be called a Builder. The Builder object has all the logic to create and initialize a service instance. You will implement a Builder object for each of the supported services.

最好的方法是定义一种提供通用接口并负责创建具体服务的新型对象。 这种新类型的对象将称为BuilderBuilder对象具有创建和初始化服务实例的所有逻辑。 您将为每个受支持的服务实现一个Builder对象。

Let’s start by looking at the application configuration:

让我们从查看应用程序配置开始:

 # In program.py

# In program.py

config config = = {
    {
    'spotify_client_key''spotify_client_key' : : 'THE_SPOTIFY_CLIENT_KEY''THE_SPOTIFY_CLIENT_KEY' ,
    ,
    'spotify_client_secret''spotify_client_secret' : : 'THE_SPOTIFY_CLIENT_SECRET''THE_SPOTIFY_CLIENT_SECRET' ,
    ,
    'pandora_client_key''pandora_client_key' : : 'THE_PANDORA_CLIENT_KEY''THE_PANDORA_CLIENT_KEY' ,
    ,
    'pandora_client_secret''pandora_client_secret' : : 'THE_PANDORA_CLIENT_SECRET''THE_PANDORA_CLIENT_SECRET' ,
    ,
    'local_music_location''local_music_location' : : '/usr/data/music'
'/usr/data/music'
}
}

The config dictionary contains all the values required to initialize each of the services. The next step is to define an interface that will use those values to create a concrete implementation of a music service. That interface will be implemented in a Builder.

config字典包含初始化每个服务所需的所有值。 下一步是定义一个接口,该接口将使用这些值创建音乐服务的具体实现。 该接口将在Builder实现。

Let’s look at the implementation of the SpotifyService and SpotifyServiceBuilder:

让我们看一下SpotifyServiceSpotifyServiceBuilder

Note: The music service interface defines a .test_connection() method, which should be enough for demonstration purposes.

注意:音乐服务接口定义了.test_connection()方法,该方法足以用于演示目的。

The example shows a SpotifyServiceBuilder that implements .__call__(spotify_client_key, spotify_client_secret, **_ignored).

该示例显示了SpotifyServiceBuilder实现.__call__(spotify_client_key, spotify_client_secret, **_ignored)

This method is used to create and initialize the concrete SpotifyService. It specifies the required parameters and ignores any additional parameters provided through **._ignored. Once the access_code is retrieved, it creates and returns the SpotifyService instance.

此方法用于创建和初始化具体的SpotifyService 。 它指定必需的参数,并忽略通过**._ignored提供的任何其他参数。 一旦access_code被检索,它创建并返回SpotifyService实例。

Notice that SpotifyServiceBuilder keeps the service instance around and only creates a new one the first time the service is requested. This avoids going through the authorization process multiple times as specified in the requirements.

请注意, SpotifyServiceBuilder保留服务实例,并且仅在首次请求服务时创建一个新实例。 这样可以避免多次按照要求指定授权过程。

Let’s do the same for Pandora:

让我们对Pandora做同样的事情:

 # In music.py

# In music.py

class class PandoraServicePandoraService :
    :
    def def __init____init__ (( selfself , , consumer_keyconsumer_key , , consumer_secretconsumer_secret ):
        ):
        selfself .. _key _key = = consumer_key
        consumer_key
        selfself .. _secret _secret = = consumer_secret

    consumer_secret

    def def test_connectiontest_connection (( selfself ):
        ):
        printprint (( ff 'Accessing Pandora with 'Accessing Pandora with  {self._key}{self._key}  and  and  {self._secret}{self._secret} '' )


)


class class PandoraServiceBuilderPandoraServiceBuilder :
    :
    def def __init____init__ (( selfself ):
        ):
        selfself .. _instance _instance = = None

    None

    def def __call____call__ (( selfself , , pandora_client_keypandora_client_key , , pandora_client_secretpandora_client_secret , , **** _ignored_ignored ):
        ):
        if if not not selfself .. _instance_instance :
            :
            consumer_keyconsumer_key , , consumer_secret consumer_secret = = selfself .. authorizeauthorize (
                (
                pandora_client_keypandora_client_key , , pandora_client_secretpandora_client_secret )
            )
            selfself .. _instance _instance = = PandoraServicePandoraService (( consumer_keyconsumer_key , , consumer_secretconsumer_secret )
        )
        return return selfself .. _instance

    _instance

    def def authorizeauthorize (( selfself , , keykey , , secretsecret ):
        ):
        return return 'PANDORA_CONSUMER_KEY''PANDORA_CONSUMER_KEY' , , 'PANDORA_CONSUMER_SECRET'
'PANDORA_CONSUMER_SECRET'

The PandoraServiceBuilder implements the same interface, but it uses different parameters and processes to create and initialize the PandoraService. It also keeps the service instance around, so the authorization only happens once.

PandoraServiceBuilder实现相同的接口,但是它使用不同的参数和过程来创建和初始化PandoraService 。 它还会保留服务实例,因此授权仅发生一次。

Finally, let’s take a look at the local service implementation:

最后,让我们看一下本地服务的实现:

The LocalService just requires a location where the collection is stored to initialize the LocalService.

LocalService只需要存储集合的位置即可初始化LocalService

A new instance is created every time the service is requested because there is no slow authorization process. The requirements are simpler, so you don’t need a Builder class. Instead, a function returning an initialized LocalService is used. This function matches the interface of the .__call__() methods implemented in the builder classes.

每次请求服务时都会创建一个新实例,因为没有缓慢的授权过程。 要求比较简单,因此您不需要Builder类。 而是使用返回初始化的LocalService的函数。 此函数与在构建器类中实现的.__call__()方法的接口匹配。

对象工厂的通用接口 (A Generic Interface to Object Factory)

A general purpose Object Factory (ObjectFactory) can leverage the generic Builder interface to create all kinds of objects. It provides a method to register a Builder based on a key value and a method to create the concrete object instances based on the key.

通用对象工厂( ObjectFactory )可以利用通用的Builder接口来创建各种对象。 它提供了一个注册的方法Builder基于一个key值,并且创建基于具体对象实例的方法key

Let’s look at the implementation of our generic ObjectFactory:

让我们看看我们的通用ObjectFactory的实现:

 # In object_factory.py

# In object_factory.py

class class ObjectFactoryObjectFactory :
    :
    def def __init____init__ (( selfself ):
        ):
        selfself .. _builders _builders = = {}

    {}

    def def register_builderregister_builder (( selfself , , keykey , , builderbuilder ):
        ):
        selfself .. _builders_builders [[ keykey ] ] = = builder

    builder

    def def createcreate (( selfself , , keykey , , **** kwargskwargs ):
        ):
        builder builder = = selfself .. _builders_builders .. getget (( keykey )
        )
        if if not not builderbuilder :
            :
            raise raise ValueErrorValueError (( keykey )
        )
        return return builderbuilder (( **** kwargskwargs )
)

The implementation structure of ObjectFactory is the same you saw in SerializerFactory.

ObjectFactory的实现结构与SerializerFactory看到的相同。

The difference is in the interface that exposes to support creating any type of object. The builder parameter can be any object that implements the callable interface. This means a Builder can be a function, a class, or an object that implements .__call__().

区别在于接口支持创建任何类型的对象。 builder参数可以是实现可调用接口的任何对象。 这意味着Builder可以是实现.__call__()的函数,类或对象。

The .create() method requires that additional arguments are specified as keyword arguments. This allows the Builder objects to specify the parameters they need and ignore the rest in no particular order. For example, you can see that create_local_music_service() specifies a local_music_location parameter and ignores the rest.

.create()方法要求将其他参数指定为关键字参数。 这使Builder对象可以指定所需的参数,而无需按特定顺序忽略其余参数。 例如,您可以看到create_local_music_service()指定了local_music_location参数,而忽略了其余参数。

Let’s create the factory instance and register the builders for the services you want to support:

让我们创建工厂实例并注册要支持的服务的构建器:

The music module exposes the ObjectFactory instance through the factory attribute. Then, the builders are registered with the instance. For Spotify and Pandora, you register an instance of their corresponding builder, but for the local service, you just pass the function.

music模块通过factory属性公开ObjectFactory实例。 然后,将构建器注册到实例。 对于Spotify和Pandora,您可以注册其相应构建器的实例,但是对于本地服务,只需传递该函数。

Let’s write a small program that demonstrates the functionality:

让我们编写一个演示该功能的小程序:

 # In program.py
# In program.py
import import music

music

config config = = {
    {
    'spotify_client_key''spotify_client_key' : : 'THE_SPOTIFY_CLIENT_KEY''THE_SPOTIFY_CLIENT_KEY' ,
    ,
    'spotify_client_secret''spotify_client_secret' : : 'THE_SPOTIFY_CLIENT_SECRET''THE_SPOTIFY_CLIENT_SECRET' ,
    ,
    'pandora_client_key''pandora_client_key' : : 'THE_PANDORA_CLIENT_KEY''THE_PANDORA_CLIENT_KEY' ,
    ,
    'pandora_client_secret''pandora_client_secret' : : 'THE_PANDORA_CLIENT_SECRET''THE_PANDORA_CLIENT_SECRET' ,
    ,
    'local_music_location''local_music_location' : : '/usr/data/music'
'/usr/data/music'
}

}

pandora pandora = = musicmusic .. factoryfactory .. createcreate (( 'PANDORA''PANDORA' , , **** configconfig )
)
pandorapandora .. test_connectiontest_connection ()

()

spotify spotify = = musicmusic .. factoryfactory .. createcreate (( 'SPOTIFY''SPOTIFY' , , **** configconfig )
)
spotifyspotify .. test_connectiontest_connection ()

()

local local = = musicmusic .. factoryfactory .. createcreate (( 'LOCAL''LOCAL' , , **** configconfig )
)
locallocal .. test_connectiontest_connection ()

()

pandora2 pandora2 = = musicmusic .. servicesservices .. getget (( 'PANDORA''PANDORA' , , **** configconfig )
)
printprint (( ff 'id(pandora) == id(pandora2): {id(pandora) == id(pandora2)}''id(pandora) == id(pandora2): {id(pandora) == id(pandora2)}' )

)

spotify2 spotify2 = = musicmusic .. servicesservices .. getget (( 'SPOTIFY''SPOTIFY' , , **** configconfig )
)
printprint (( ff 'id(spotify) == id(spotify2): {id(spotify) == id(spotify2)}''id(spotify) == id(spotify2): {id(spotify) == id(spotify2)}' )
)

The application defines a config dictionary representing the application configuration. The configuration is used as the keyword arguments to the factory regardless of the service you want to access. The factory creates the concrete implementation of the music service based on the specified key parameter.

应用程序定义了代表应用程序配置的config字典。 无论您要访问什么服务,该配置都将用作工厂的关键字参数。 工厂根据指定的key参数创建音乐服务的具体实现。

You can now run our program to see how it works:

现在,您可以运行我们的程序以查看其工作方式:

You can see that the correct instance is created depending on the specified service type. You can also see that requesting the Pandora or Spotify service always returns the same instance.

您可以看到根据指定的服务类型创建了正确的实例。 您还可以看到请求Pandora或Spotify服务始终返回相同的实例。

专业化对象工厂以提高代码可读性 (Specializing Object Factory to Improve Code Readability)

General solutions are reusable and avoid code duplication. Unfortunately, they can also obscure the code and make it less readable.

通用解决方案是可重用的,并且可以避免代码重复。 不幸的是,他们也可能使代码晦涩难懂,并使代码的可读性降低。

The example above shows that, to access a music service, music.factory.create() is called. This may lead to confusion. Other developers might believe that a new instance is created every time and decide that they should keep around the service instance to avoid the slow initialization process.

上面的示例显示,要访问音乐服务,将调用music.factory.create() 。 这可能导致混乱。 其他开发人员可能会相信每次都会创建一个新实例,并决定他们应该保留在该服务实例周围,以避免缓慢的初始化过程。

You know that this is not what happens because the Builder class keeps the initialized instance and returns it for subsequent calls, but this isn’t clear from just reading the code.

您知道不会发生这种情况,因为Builder类保留了初始化的实例并为后续调用返回该实例,但是仅读取代码尚不清楚。

A good solution is to specialize a general purpose implementation to provide an interface that is concrete to the application context. In this section, you will specialize ObjectFactory in the context of our music services, so the application code communicates the intent better and becomes more readable.

一个好的解决方案是专门化通用实现,以提供与应用程序上下文具体相关的接口。 在本部分中,您将在我们的音乐服务的上下文中专注于ObjectFactory ,因此应用程序代码可以更好地传达意图并变得更具可读性。

The following example shows how to specialize ObjectFactory, providing an explicit interface to the context of the application:

下面的示例演示如何专门化ObjectFactory ,为应用程序的上下文提供显式接口:

 # In music.py

# In music.py

class class MusicServiceProviderMusicServiceProvider (( object_factoryobject_factory .. ObjectFactoryObjectFactory ):
    ):
    def def getget (( selfself , , service_idservice_id , , **** kwargskwargs ):
        ):
        return return selfself .. createcreate (( service_idservice_id , , **** kwargskwargs )


)


services services = = MusicServiceProviderMusicServiceProvider ()
()
servicesservices .. register_builderregister_builder (( 'SPOTIFY''SPOTIFY' , , SpotifyServiceBuilderSpotifyServiceBuilder ())
())
servicesservices .. register_builderregister_builder (( 'PANDORA''PANDORA' , , PandoraServiceBuilderPandoraServiceBuilder ())
())
servicesservices .. register_builderregister_builder (( 'LOCAL''LOCAL' , , create_local_music_servicecreate_local_music_service )
)

You derive MusicServiceProvider from ObjectFactory and expose a new method .get(service_id, **kwargs).

您从ObjectFactory派生MusicServiceProvider并公开了一个新方法.get(service_id, **kwargs)

This method invokes the generic .create(key, **kwargs), so the behavior remains the same, but the code reads better in the context of our application. You also renamed the previous factory variable to services and initialized it as a MusicServiceProvider.

该方法调用通用的.create(key, **kwargs) ,因此行为保持不变,但是在我们的应用程序上下文中,代码.create(key, **kwargs)更好。 您还将先前的factory变量重命名为services ,并将其初始化为MusicServiceProvider

As you can see, the updated application code reads much better now:

如您所见,更新后的应用程序代码现在读取效果更好:

Running the program shows that the behavior hasn’t changed:

运行程序表明行为没有改变:

 $ python program.py
$ python program.py
Accessing Pandora with PANDORA_CONSUMER_KEY and PANDORA_CONSUMER_SECRET
Accessing Pandora with PANDORA_CONSUMER_KEY and PANDORA_CONSUMER_SECRET
Accessing Spotify with SPOTIFY_ACCESS_CODE
Accessing Spotify with SPOTIFY_ACCESS_CODE
Accessing Local music at /usr/data/music
Accessing Local music at /usr/data/music
id(pandora) == id(pandora2): True
id(pandora) == id(pandora2): True
id(spotify) == id(spotify2): True
id(spotify) == id(spotify2): True

结论 (Conclusion)

Factory Method is a widely used, creational design pattern that can be used in many situations where multiple concrete implementations of an interface exist.

工厂方法是一种广泛使用的创新设计模式,可以在存在接口的多个具体实现的许多情况下使用。

The pattern removes complex logical code that is hard to maintain, and replaces it with a design that is reusable and extensible. The pattern avoids modifying existing code to support new requirements.

该模式除去了难以维护的复杂逻辑代码,并用可重用和可扩展的设计代替了它。 该模式避免修改现有代码以支持新需求。

This is important because changing existing code can introduce changes in behavior or subtle bugs.

这很重要,因为更改现有代码可能会导致行为更改或细微的错误。

In this article, you learned:

在本文中,您了解了:

  • What the Factory Method design pattern is and what its components are
  • How to refactor existing code to leverage Factory Method
  • Situations in which Factory Method should be used
  • How Object Factories provide more flexibility to implement Factory Method
  • How to implement a general purpose Object Factory and its challenges
  • How to specialize a general solution to provide a better context
  • 什么是工厂方法设计模式及其组成部分
  • 如何重构现有代码以利用工厂方法
  • 应使用工厂方法的情况
  • 对象工厂如何提供更大的灵活性来实施工厂方法
  • 如何实现通用对象工厂及其挑战
  • 如何专门化通用解决方案以提供更好的环境

进一步阅读 (Further Reading)

If you want to learn more about Factory Method and other design patterns, I recommend Design Patterns: Elements of Reusable Object-Oriented Software by the GoF, which is a great reference for widely adopted design patterns.

如果您想了解有关Factory Method和其他设计模式的更多信息,我建议GoF 设计模式:可重用的面向对象软件的元素 ,这对于广泛采用的设计模式是一个很好的参考。

Also, Heads First Design Patterns: A Brain-Friendly Guide by Eric Freeman and Elisabeth Robson provides a fun, easy-to-read explanation of design patterns.

此外,由Eric Freeman和Elisabeth Robson撰写的《 Heads First Design Patterns:A Brain-Friendly Guide》 (埃里克·弗里曼(Eric Freeman)和Elisabeth Robson)提供了有趣,易于阅读的设计模式说明。

Wikipedia has a good catalog of design patterns with links to pages for the most common and useful patterns.

维基百科上有很好的设计模式目录,其中包含指向最常见和最有用模式的页面的链接。

翻译自: https://www.pybloggers.com/2019/02/the-factory-method-pattern-and-its-implementation-in-python/

python 实现工厂模式

你可能感兴趣的:(设计模式,大数据,编程语言,python,机器学习)