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:
在本文末尾,您将 :
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技能提升到新水平所需的路线图和心态。
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 Love Dire 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 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()
方法的情况下实现需求的任何更改。 接下来的部分让我们看看如何做到这一点。
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设计模式的所需结构。
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.
新版本的代码更易于阅读和理解,但仍可以通过工厂方法的基本实现进行改进。
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 Love Dire 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
。
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设计模式来提供更好的设计。
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
表示形式。 似乎该应用程序提供了与音乐相关的功能,因此该应用程序需要序列化其他类型的对象(如Playlist
或Album
)似乎是合理的。
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
.
该接口由具体的类JsonSerializer
和XmlSerializer
。
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
的实现是完全通用的,并且仅提及可serializable
的format
和format
作为参数。
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
。
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 Love Dire 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
是一个类。
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 Love Dire 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.
通过使用对象工厂实现工厂方法并提供注册接口,您可以支持新格式,而无需更改任何现有应用程序代码。 这样可以最大程度地降低破坏现有功能或引入细微错误的风险。
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.
提供对象工厂的通用实现存在一些挑战,在以下部分中,您将研究这些挑战并实现可在任何情况下重用的解决方案。
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来解决此问题。
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.
最好的方法是定义一种提供通用接口并负责创建具体服务的新型对象。 这种新类型的对象将称为Builder
。 Builder
对象具有创建和初始化服务实例的所有逻辑。 您将为每个受支持的服务实现一个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
:
让我们看一下SpotifyService
和SpotifyServiceBuilder
:
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 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服务始终返回相同的实例。
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
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:
在本文中,您了解了:
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 实现工厂模式