Python 搭建插件式框架(基于组件开发) - 转载自伊壬同学的博客

概念

基于组件的开发(Component-Based Development,简称 CBD)是一种软件开发范型。它是现今软件复用理论实用化的研究热点,在组件对象模型的支持下,通过复用已有的构件,软件开发者可以 “即插即用” 地快速构造应用软件。

优点

  • 灵活性高:各个功能模块之间的耦合很低,每一个组件都是独立的,它附着在整个插件框架上执行,真正的实现有则加载,无则忽略。

  • 复用性强:由于组件之间的通信或者交互都是通过插件框架提供的接口来执行,各个组件之见是遵守依赖倒置原则的。所以无论需要哪个模块的功能,都只需要将该插件直接拿走复用即可。

框架描述

之前有这么一个有趣的笑话。说一个人一大早起来想吃火锅,但是他又不想出门,于是他想了个主意,他给 A 打电话说:今天请大家吃火锅,别的东西都有了,就差一份羊肉了,来的时候带着。完了给 B 打电话说:今天请大家吃火锅,别的东西都有的,火锅料忘了买了,来的时候捎上……,他用这样的方法将所有的菜凑够,足不出户,就能吃火锅,而且想吃啥就吃啥。

这个例子中,这个在家里想吃火锅并且挨个给大家打电话的人便是插件式框架中的总框架,本身不提供任何的功能,角色就是总指挥。而小 A,小 B 这些朋友则是各个组件,自己只负责自己的部分,但是每一个组件都无法单独执行,只能在总框架中执行。组件为整个开发提供基本的功能,组件之间的通信也都是通过总框架来实现的,这就是整个插件式框架。

实现

相信点开看这篇文章的都是有一定 Python 基础并且遇到类似于插件式开发需求,从而来看一份有用的代码,再将其拷贝走的。话不多说,上代码吧。

目录结构

++PluginFrame

– main.py

– PluginManager

++ Plugins

-- Plugin1.py

-- Plugin2.py

-- Plugin3.py

-- Plugin4.py

### 插件式框架
import os
import sys
from imp import find_module
from imp import load_module

class PluginManager(type):
    #静态变量配置插件路径
    __PluginPath = 'Plugins'

    #调用时将插件注册
    def __init__(self,name,bases,dict):
        if not hasattr(self,'AllPlugins'):
            self.__AllPlugins = {}
        else:
            self.RegisterAllPlugin(self)

    #设置插件路径
    @staticmethod
    def SetPluginPath(path):
        if os.path.isdir(path):
            PluginManager.__PluginPath = path
        else:
            print '%s is not a valid path' % path

    #递归检测插件路径下的所有插件,并将它们存到内存中
    @staticmethod
    def LoadAllPlugin():
        pluginPath = PluginManager.__PluginPath
        if not os.path.isdir(pluginPath):
            raise EnvironmentError,'%s is not a directory' % pluginPath

        items = os.listdir(pluginPath)
        for item in items:
            if os.path.isdir(os.path.join(pluginPath,item)):
                PluginManager.__PluginPath = os.path.join(pluginPath,item)
                PluginManager.LoadAllPlugin()
            else:
                if item.endswith('.py') and item != '__init__.py':
                    moduleName = item[:-3]
                    if moduleName not in sys.modules:
                        fileHandle, filePath,dect = find_module(moduleName,[pluginPath])
                    try:
                        moduleObj = load_module(moduleName,fileHandle,filePath,dect)
                    finally:
                        if fileHandle : fileHandle.close()

    #返回所有的插件
    @property
    def AllPlugins(self):
        return self.__AllPlugins

    #注册插件
    def RegisterAllPlugin(self,aPlugin):
        pluginName = '.'.join([aPlugin.__module__,aPlugin.__name__])
        pluginObj = aPlugin()
        self.__AllPlugins[pluginName] = pluginObj

    #注销插件
    def UnregisterPlugin(self,pLuginName):
        if pluginName in self.__AllPlugins:
            pluginObj = self.__AllPlugins[pluginName]
            del pluginObj

    #获取插件对象。
    def GetPluginObject(self, pluginName = None):
        if pluginName is None:
            return self.__AllPlugins.values()
        else:
            result = self.__AllPlugins[pluginName] if pluginName in self.__AllPlugins else None
            return result

    #根据插件名字,获取插件对象。(提供插件之间的通信)
    @staticmethod
    def GetPluginByName(pluginName):
        if pluginName is None:
            return None
        else:
            for SingleModel in __ALLMODEL__:
                plugin = SingleModel.GetPluginObject(pluginName)
                if plugin:
                    return plugin

#插件框架的接入点。便于管理各个插件。各个插件通过继承接入点类,利用Python中metaclass的优势,将插件注册。接入点中定义了各个插件模块必须要实现的接口。
class Model_Component(object):
    __metaclass__ = PluginManager

    def Start(self):
        print 'Please write the Start() function'

    def ChangeLanguage(self,language):
        print 'Please write the ChangeLanguage() function'

class Model_MenuObj(object):
    __metaclass__ = PluginManager

    def Start(self):
        print 'Please write the Start() function'

    def ChangeLanguage(self,language):
        print 'Please write the ChangeLanguage() function'

class Model_ToolBarObj(object):
    __metaclass__ = PluginManager

    def Start(self):
        print 'Please write the Start() function'

    def ChangeLanguage(self,language):
        print 'Please write the ChangeLanguage() function'

class Model_ParamPanelObj(object):
    __metaclass__ = PluginManager

    def Start(self):
        print 'Please write the Start() function'

    def ChangeLanguage(self,language):
        print 'Please write the ChangeLanguage() function'

__ALLMODEL__ = (Model_ParamPanelObj,Model_ToolBarObj,Model_MenuObj,Model_Component)
#插件1
from PluginManager import Model_MenuObj

class Plugin1(Model_MenuObj):
    def __init__(self):
        pass

    #实现接入点的接口
    def Start(self):
        print "I am plugin1 , I am a menu!"
#插件2
from PluginManager import Model_ToolBarObj

class Plugin2(Model_ToolBarObj):
    def __init__(self):
        pass

    def Start(self):
        print "I am plugin2 , I am a ToolBar!"
#插件3
from PluginManager import Model_ParamPanelObj

class Plugin3(Model_ParamPanelObj):
    def __init__(self):
        pass

    def Start(self):
        print "I am plugin3 , I am a ParamPanel!"
#插件4
from PluginManager import Model_Component

class Plugin4(Model_Component):
    def __init__(self):
        pass

    def Start(self):
        print "I am plugin4 , I am a Component!"
#main调用

import sys
from PluginManager import PluginManager
from PluginManager import __ALLMODEL__

if __name__ == '__main__':
    #加载所有插件
    PluginManager.LoadAllPlugin()

    #遍历所有接入点下的所有插件
    for SingleModel in __ALLMODEL__:
        plugins = SingleModel.GetPluginObject()
        for item in plugins:

            #调用接入点的公共接口
            item.Start()

输出

I am plugin3 , I am a ParamPanel!

I am plugin2 , I am a ToolBar!

I am plugin1 , I am a menu!

I am plugin4 , I am a Component!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342