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()方法支持两种不同的格式: JSONXML 。 不支持指定的任何其他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' )
'<song id="1"><title>Water of Love</title><artist>Dire Straits</artist></song>'

>>>  serializer . serialize ( song , 'YAML' )
Traceback (most recent call last):
  File "<stdin>" , line 1 , in <module>
  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类添加或删除属性将需要更改实现以适应新结构。

  • 当格式的字符串表示形式更改时(纯JSONJSON 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 forma
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值