Python實現IOC控制反轉

思路:

用一個字典存儲beanName和資源
初始化時先將beanName和資源註冊到字典中
然後用一個Dscriptor類根據beanName動態請求資源,從而實現控制反轉

# -*- coding:utf-8 -*-
import os
class BeanFactory: 
    """
    Python版控制反轉
    context: 存儲bean的名字和對應的類或者值的字典
    allowRepalce: 是否允許替換已經注入的bean
    """

    def __init__(self,allowReplace=False):
        """構造函數
        allowReplace:是否允許替換已經注入的bean
        """
        self.context = {}
        self.allowReplace = allowReplace
    def setBean(self,beanName,resource,*args,**kwargs):
        if not self.allowReplace:
            assert not beanName in self.context,"該BeanFactory不允許重複注入%r,請修改beanName" % beanName
        def call():
            """定義一個函數閉包,如果注入的resource是可調用類型,就將*args和**kwargs傳入並調用該函數,然後將返回值返回
            如果是一個不可調用對象,就直接返回
            """
            if callable(resource):
                return resource(*args,**kwargs)
            else:
                return resource
        #將call閉包與beanName建立映射
        self.context[beanName]=call
    def __getitem__(self,beanName):
        """重載__getitem__方法,使得BeanFactory支持使用[]獲取beanName對應的註冊的資源
        """
        try:
            # 從context字典中取出beanName對應的資源
            resource = self.context[beanName]
        except KeyError:
            raise KeyError("%r 未註冊" % beanName)
        # 返回閉包函數調用後的結果
        return resource()

AppFactory = BeanFactory()

def NoAssertion(obj): return True

def IsInstanceOf(*classes):
   def test(obj): return isinstance(obj, classes)
   return test

def HasAttributes(*attributes):
   def test(obj):
      for each in attributes:
         if not hasattr(obj, each): return False
      return True
   return test

def HasMethods(*methods):
   def test(obj):
      for each in methods:
         try:
            attr = getattr(obj, each)
         except AttributeError:
            return False
         if not callable(attr): return False
      return True
   return test


#
#
#Descriptor就是一類實現了__get__(), __set__(), __delete__()方法的對象
#若一個類的成員是descriptor,在訪問它的值時是通過__get__()函數訪問的
#用這個特性實現在訪問一個類的成員時自動到BeanFactory中請求對應的資源

class RequiredResource(object):
   def __init__(self, beanName, assertion=NoAssertion):
      self.beanName =  beanName
      self.assertion = assertion
   def __get__(self, obj, T):#每次訪問descriptor時都會調用__get__方法
      return self.result # <-- .操作符會自動調用__getattr__
   def __getattr__(self, name):
      assert name == 'result', "Unexpected attribute request other then 'result'"
      self.result = self.Request()
      return self.result
   def Request(self):
      obj = AppFactory[self.beanName]
      assert self.assertion(obj), \
             "The value %r of %r does not match the specified criteria" \
             % (obj, self.feature)
      return obj




class Component(object):
   "Symbolic base class for components"
class Bar(Component):
   # HasMethods是一個閉包函數,傳入RequiredResource後用於檢查'Console'
   # 對應的註冊的那個feature是否有'WriteLine'方法
   # IsinstanceOf(str) 是一個閉包,傳入RequiredResource後會被調用,用於
   # 檢查註冊的'AppTitle'對應資源是否是一個字符串
   # IsinstanceOf(str) 是一個閉包,傳入RequiredResource後會被調用,用於
   # 檢查'CurrentUser'對應的資源是否是一個字符串

   # RequiredFeatuire是desciptor,每次訪問descriptor(即實現了__get__的類),都會先經過__get__函數。

   con   = RequiredResource('Console', HasMethods('WriteLine'))
   title = RequiredResource('AppTitle', IsInstanceOf(str))
   user  = RequiredResource('CurrentUser', IsInstanceOf(str))
   flist = RequiredResource('show_dir',IsInstanceOf(list))
   def __init__(self):
      self.X = 0
   def PrintYourself(self):
      self.con.WriteLine('-- Bar instance --')
      # title 由 RequiredResource('AppTitle',IsInstanceOf(str))生成
      #'AppTitle'對應的值在__main__代碼塊註冊了一個值
      self.con.WriteLine('Title: %s' % self.title)
      self.con.WriteLine('User: %s' % self.user)
      self.con.WriteLine('X: %d' % self.X)
      for f in self.flist:
           self.con.WriteLine(f)

class BetterConsole(Component):
   def __init__(self, prefix=''):
      self.prefix = prefix
   def WriteLine(self, s):
      lines = s.split('\n')
      for line in lines:
         if line:
            print(self.prefix, line)
         else:
            print


def GetCurrentUser():
   return os.getenv('USERNAME') or 'Some User' # USERNAME is platform-specific
def ShowDir():
    return os.listdir()

if __name__ == '__main__':
   print('\n*** IoC Demo ***')
   #Provide(feature,provider,*args,**kwargs) feature是要生成的對象的父類類型 provider是要注入的子類或者值
   AppFactory.setBean('AppTitle', 'Inversion of Control ...\n\n... The Python Way')
   AppFactory.setBean('CurrentUser', GetCurrentUser)
   AppFactory.setBean('Console', BetterConsole, prefix='-->') # <-- transient lifestyle
   AppFactory.setBean('show_dir',ShowDir)

   bar = Bar()
   bar.PrintYourself()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章