上一篇文章分享了简单工厂模式的使用,在运行时根据客户端传入的参数类型来创建相应的实例。本节进一步讨论简单工厂的变体工厂方法模式。
工厂方法模式:是一种创建型设计模式,向客户端开放一个创建对象的方法,使用继承和子类来决定创建哪个对象。工厂方法用于创建单个产品。
1、具有更大的灵活性,使得代码更加通用。实现哪些类取决于接口,而不是具体产品类。
2、工厂方法将创建对象的代码与实际使用它的代码是分开的,松耦合的,从而能在不影响其他代码情况下扩展产品创建部分代码。添加新类变得更加容易,降低了维护成本。
可使用工厂方法模式应用场景
1、编写代码初期版本过程中,如果无法预知对象确切类别及其依赖关系。
2、二次开发,继承重写方法,扩展模块或框架的内部组件。
3、复用现有对象节省系统资源,而不是每次重新创建对象。
4、对象池管理及权限管理。
工厂方法模式类图
←工厂方法模式类图→
1、产品(Product):对接口进行定义。
2、具体产品(ConcreteProduct):产品接口的不同实现。
3、创建者类(Creator):申明返回产品对象的工厂方法。可以将工厂方法声明为抽象方法,强制要求每个子类以不同方式实现该方法。
4、具体创建者(ConcreteCreator):将会重写基础工厂方法,使其返回不同类型的产品。注:并不一定每次调用工厂方法都会创建新的实例,工厂方法可以返回缓存、对象池的已有对象。
工厂方法模式
本次分享结合深圳市便民核酸点服务状态查询服务数据示例。继上篇文章对应用服务端代码进一步优化设计模式。仅需修改以下代码部分。
1)、定义一个接口来创建对象,但是工厂本身并不负责创建对象,而是将该任务交由子类来完成,即子类决定了要实例化哪些类;
2)、Factory方法的创建是通过继承而不是通过实例化来完成;
3)、工厂方法使得设计更加具有可定制性。
1、应用服务端-Map_Load.py
import pandas as pd
import folium
from folium import plugins
from abc import ABCMeta,abstractmethod
#基础底图部分
class Basemap_Section(metaclass=ABCMeta):
"""
地图初始化基类
"""
def __init__(self):
#加载地图模式
self.Map = folium.Map(location=[22.540477, 114.061226],
tiles='https://wprd01.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7',
attr='高德-常规图',
zoom_start=10,
control_scale=True,
width='100%',)
@abstractmethod
def execute(self):
pass
#标记卡片部分
class Card_Section(Basemap_Section):
def execute(self,data):
import base64
encoded = base64.b64encode(open(r'F:\BuildEnergyDataHub_Admin\resources\qrcode_logo.JPG', 'rb').read()).decode()
# data映射一列数据【color】---设置数据过滤层
for name, row in data.iterrows():
html = '<h2> 欢迎关注Python数据分析实例</h2><img src="data:image/jpeg;base64,{0}"><br><h3>采样点名称:{1}</h3>' \
'<h3>当前等待时间:{2}</h3><h3>服务时间:{3}</h3><h3>服务人群:{4}</h3>' \
'<a href = https://szwj.borycloud.com/wh5/index.html#/>查询服务➡</a>'.format(encoded,row['service_name'],str(row["delay"]),row['service_time'],row['services'])
iframe = folium.IFrame(html, width=380, height=560)
return iframe
#地图一次性全加载标记
class Full_Load_Section(Basemap_Section):
"""
地图一次性全加载
"""
def execute(self,data,iframe):
for name, row in data.iterrows():
folium.Marker(
[row["LATITUDE"], row["LONGITUDE"]],
# max_width设置每行显示字符数
popup=folium.Popup(iframe,
max_width='100%'), # 备注掉线天数
tooltip=row["service_name"],
icon=folium.Icon(icon='info-sign', color=row["color"])).add_to(self.Map)
print("地图成功生成!")
return self.Map
#地图缩放加载标记
class Zoom_Loading_Section(Basemap_Section):
"""
地图缩放加载
"""
def execute(self,data,iframe):
# 调用Marker可以创建标记,传入位置和信息,当鼠标放在标记上会显示出信息。
folium.Marker([22.540477,114.061226],
popup=folium.Popup("深圳市", max_width=100)).add_to(self.Map) #创建中心标记
marker_cluster = plugins.MarkerCluster().add_to(self.Map)
for name, row in data.iterrows():
folium.Marker([row["LATITUDE"], row["LONGITUDE"]],
popup=folium.Popup(iframe, max_width='100%'),
tooltip=row["service_name"],
icon=folium.Icon(icon='info-sign', color='lightblue')).add_to(marker_cluster)
print("地图成功生成")
return self.Map
#其他功能模块
class Other_Section(Basemap_Section):
def execute(self):
# print('Other Section')
return 'Other Section'
上篇文章简单工厂模式创建了Full_Load和Zoom_Loading两种地图显示产品,包含重复代码部分,对于产品中某些特定的组成部分比如都有一个加载显示卡片区Card_Section类,我们可以进一步细化对方法层面的封装,让每一个功能类尽可能简单独立,将应用封装成具体产品类,然后通过具体产品工厂类继承加载实例化具体的工厂方法实现。
#抽象工厂类Creator
class Map_Profile(metaclass=ABCMeta):
def __init__(self):
self.sections = []
self.createPrfile()
@abstractmethod
def createPrfile(self):
pass
def getSections(self):
return self.sections
def addSections(self,section):
self.sections.append(section)
#具体工厂类
class Full_Load(Map_Profile):
"""
地图一次性全加载
"""
def __init__(self,data):
super(Full_Load, self).__init__()
#公共部分
self.data = data
def createPrfile(self):
self.addSections(Card_Section())
self.addSections(Full_Load_Section())
class Zoom_Loading(Map_Profile):
"""
地图缩放加载
"""
def __init__(self,data):
super(Zoom_Loading, self).__init__()
#公共部分
self.data = data
def createPrfile(self):
self.addSections(Card_Section())
self.addSections(Zoom_Loading_Section())
工厂方法模式实现细节:
创建一个名为Map_Profile的抽象类Creator,其提供一个工厂方法createPrfile(),Map_Profile抽象类由self.sections列表存储具体需实例化的对象。具体工厂ConcreteCreator类比如Full_Load(Map_Profile)和Zoom_Loading(Map_Profile)通过继承重写,每个类都实现了createPrfile()抽象方法,由该方法在运行时实际创建(实例化),创建需要的资源加载到相应的对象存储空间即对象池。
2、应用客户端-Map_server_client.py
from apps.tools.Map_Load import ForestFactory
class webservice_client():
def __init__(self,BaseAlgorithm,data):
# super(webservice_client, self).__init__()
# 定义客户端对象
self.BaseAlgorithm = BaseAlgorithm
self.data = data
@fail_data(msg='地图加载失败')
def get_Map_model(self):
# f = ForestFactory()
# Map = f.make_execute(self.BaseAlgorithm,self.data)
Section = eval(self.BaseAlgorithm)(self.data)
Card_Section = Section.getSections()[0].execute(self.data)
Map = Section.getSections()[1].execute(self.data,Card_Section)
return Map
实现权限管理
随着应用功能拓展,需要更好的管理组织产品功能类时,对以上抽象工厂类代码结构升级改造变体,利用字典注册功能,松耦合,功能通过注册使用,工厂方法模式实现权限管理功能。
#抽象工厂--功能注册
class Profile_registration(metaclass=ABCMeta):
def __init__(self):
self.sections = {}
self.createPrfile()
@abstractmethod
def createPrfile(self):
pass
def getSections(self):
return self.sections
def addSections(self,section):
self.sections.update({type(section).__name__:section})
class Full_Load_v1(Profile_registration):
"""
地图一次性全加载
"""
def __init__(self,data):
super(Full_Load_v1, self).__init__()
#公共部分
self.data = data
def createPrfile(self):
self.addSections(Other_Section())
self.addSections(Card_Section())
self.addSections(Full_Load_Section())
class Zoom_Loading_v1(Profile_registration):
"""
地图缩放加载
"""
def __init__(self,data):
super(Zoom_Loading_v1, self).__init__()
#公共部分
self.data = data
def createPrfile(self):
self.addSections(Card_Section())
self.addSections(Zoom_Loading_Section())
# 客服端代码测试
if __name__ =='__main__':
Profile_type = input("Which Profile you'd like to create? [Full_Load_v1 or Zoom_Loading_v1]")
data = pd.read_excel(r".\apps\Mapview\data1.xlsx",index_col=False)
Section = eval(Profile_type)(data)
Section_dict =Section.getSections()
print("Map_Profile has sections --",Section_dict)
print("Card_Section --", Section_dict.get('Card_Section'))
print("Other_Section --", Section_dict.get('Other_Section').execute())
#测试结果
Which Profile you'd like to create? [Full_Load_v1 or Zoom_Loading_v1]Full_Load_v1
Map_Profile has sections -- {'Other_Section': <__main__.Other_Section object at 0x000001429616E2B0>, 'Card_Section': <__main__.Card_Section object at 0x00000142992E2D68>, 'Full_Load_Section': <__main__.Full_Load_Section object at 0x00000142992E28D0>}
Card_Section -- <__main__.Card_Section object at 0x00000142992E2D68>
Other Section
Other_Section -- Other Section
运行代码,按要求输入要创建的具体工厂类,以Full_Load_v1为例,实例化创建具体子类Other_Section、Card_Section、Full_Load_Section。获取字典具体对象名使用实例化好的对象。自主决定实例化哪些类或使用哪些对象,对象管理更加灵活。
如果需要向应用中添加一种新产品,你只需要开发新的具体子类产品,然后重写其工厂方法即可,并在具体工厂类中注册取用。
当应用功能越来越复杂时,设计模式开始显得尤为重要,面向接口开发,而不是面向具体实现。许多项目前期初期版本会使用工厂方法模式,其较为简单,方便通过子类进行定制,随着演变为使用抽象工厂,更加灵活复杂。
抽象工厂模式:包含一个或多个工厂方法来创建一个系列的相关对象,使用组合将创建对象的任务委托给其他类。
在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。在只有一个产品的情况下,抽象工厂模式实际上退化到工厂方法模式。抽象工厂方法无需指定具体的类就能创建一系列相关对象。后期结合合适实例再详细讲解。
以上为本次分享的全部内容,文中已包含大部分源代码,在此基础上,我想可以拓展实现不同的权限显示不同的功能或模块,各位小伙伴赶快动手实践一下吧!原创不易,欢迎点赞、分享支持。
下一篇进一步研究其他类型模式,敬请期待!
本文分享自 Python数据分析实例 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!