Python国际化工具(二)策略模式的使用

in 软件设计模式 with 0 comment

我的国际化工具,是根据不同的标签类型来获得,如普通的span标签,或者是它的placeholder属性,又或者是拿JavaScript的中文。

所以,根据他们所实现的不同功能,把它们一个个封装起来,而且为了未来的可扩展性,让它们可相互替换。可以使用策略模式使得不同的操作方法独立于使用它的对象而变化。

不过,我也是为了学习软件模式,个人觉得这个场景下可以使用策略模式完成。同时,Python也是一门面向对象语言。
也算是为了使用策略模式而使用策略模式

策略模式类图

image.png

策略模式的类图一般就是长这个样子。
定一个父类---策略类
它的子类就是完成不同的业务操作。比如我的就是如图所示的三个不同的业务操作。
最后,再将这个类统一给上下文类管理。

我的工具策略模式的好处

单从我这个场景来说。根据抽象的思想,如果我首先完成了获得了普通的tag标签方法。我现在又要去完成获得特殊label标签的方法。然后又要去完成获得script内中文的方法。。。等等

那么,每次我又要新增一个功能的时候,我又要去增加或者修改原类的代码。这样很不易维护,违背了软件设计模式的开闭原则。

所以,使用策略模式,针对这个场景,那么,我只需要通过子类的方式,实现父类的getTextCN()方法,再在子类中写相应的逻辑操作即可。

我的工具策略模式的坏处

继续我这个场景,就像我说的,我现在只是三个策略类,完成三个不同的逻辑操作。
那么,我如果有10个操作,或者20个,100个,那么我就要写100个子类去实现父类的方法。
所以,对于策略模式的坏处,就是会造成许多的策略类。所以,这个就是策略模式不好的地方。

代码

策略类父类

class TextSuper:
    def getTextCN(self, label, html):
        pass

获得tags中文子类

class TagsCN(TextSuper):
    def getTextCN(self, tag, html):
        CNTexts = []
        resultList = []
        # 排除注解
        html = unNode(html)
        pat = r"<" + tag + r".*>.*</" + tag + ">"
        patSearch = r"<" + tag + r".*>(.*)</" + tag + ">"
        tagHtmls = re.findall(pat, html)
        for tagHtml in tagHtmls:
            CNHtmls = re.search(patSearch, tagHtml)
            if CNHtmls:
                flag = re.match(r'[\u4e00-\u9fa5]', CNHtmls.group(1))
                if flag:
                    CNTexts.append(CNHtmls.group(1))
        # 去重
        resultList = list(set(CNTexts))
        return resultList

获得labels中文子类

class LabelsCN(TextSuper):
    def getTextCN(self, label, html):
        CNTexts = []
        resultList = []
        # 排除注解
        html = unNode(html)
        pat = r'[^:]' + label + r'=[\"|\'][^\"]*[\"|\']'
        patSearch = r'[^:]' + label + r'=[\"|\']([^\"]*)[\"|\']'
        # 获得那整行
        labelHtmls = re.findall(pat, html)
        for labelHtml in labelHtmls:
            CNHtmls = re.search(patSearch, labelHtml)
            if CNHtmls:
                flag = re.match(r'[\u4e00-\u9fa5]', CNHtmls.group(1))
                if flag:
                    CNTexts.append(CNHtmls.group(1))
            # 去重
        resultList = list(set(CNTexts))
        return resultList

获得script中文子类

class ScriptCN(TextSuper):
    def getTextCN(self, script, html):
        CNList = []
        resultList = []
        # 排除注解
        html = unNode(html)
        scriptAllHtml = re.findall(r'<script>[\w\W]*<\/script>', html)
        pat = script + r'[:|\(|\'|\"|= ]+[^;|^)|^\"|^}|,]*[)|\"|\']'
        patSearch = script + r'[:|\(|\'|\"|=| ]+([^;|^)|^\"|^}|,]*)[)|\"|\']'
        for html in scriptAllHtml:
            scriptHtmls = re.findall(pat, html)
            for scriptHtml in scriptHtmls:
                CNHtmls = re.search(patSearch, scriptHtml)
                if CNHtmls:
                    flag = re.findall(r'[\u4e00-\u9fa5]', CNHtmls.group(1))
                    if flag:
                        CNList.append(CNHtmls.group(1))
            # 去重
        resultList = list(set(CNList))
        return resultList

上下文使用类

class Context:
    def __init__(self, text_super):
        self.text_super = text_super

    def GetJsonText(self, label, html):
        return self.text_super.getTextCN(self, label, html)

使用

现在代码就是和我的类图一样的结构。
在使用时,我们使用不同的test_super就行了。

代码的使用

def getDatas(pathRoot, lists ,TextSuper):
    AllText = {}
    tagList = []
    fileNameList = operationFile.getFileName(pathRoot)
    print("一共有" + str(len(fileNameList)) + "个vue文件")
    for listData in lists:
        # 进入循环taglist清空
        tagList = []
        for fileName in fileNameList:
            # 获得文本
            html = operationFile.readHTML(fileName)
	    # 根据不同的策略类调用不同的方法
            datas = Context(TextSuper).GetJsonText(listData,html)
            for data in datas:
                tagList.append(data)
            # 再次去重
        tagList = list(set(tagList))
        jsonMap = formatText.getMap(tagList)
        # 将jsonMap装箱
        AllText[listData] = jsonMap
    return AllText

# tags集合
tags = ['el-button', 'span', 'el-dropdown-item', 'th', 'el-tag' , 'th', 'td', 'label']

# labels集合
lables = ['placeholder', 'title', 'content', 'label']

# script集合
scripts = ['error','message','warning','success','value','title']

def main():
    # 传入集合和策略类
    resultMapDict = getDatas.getDatas(pathRoot, scripts,ScriptCN)
    bJson = json.dumps(resultMapDict, ensure_ascii=False, sort_keys=True, indent=2)
    # 写入文件
    operationFile.write(bJson, outFilePath)

就这样完成了这个小操作的策略模式的使用
使用的核心代码就是

 # 根据不同的策略类调用不同的方法
 datas = Context(TextSuper).GetJsonText(listData,html)

通过上下文类的TextSuper这个父类,子类继承自父类,所以,可以通过main方法的

 # 传入集合和策略类
 resultMapDict = getDatas.getDatas(pathRoot, scripts,ScriptCN)

通过传参,调用不同的子类。

策略模式给我的带来的好处

说来可能不信,策略模式给我节省了至少100行代码,因为在调用不同的方法的时候,我核心的方法就是那些策略子类嘛,然后因为调用的方法不同,在我加入策略模式之前,我有两个方式完成调用不同的方法

def getLables(pathRoot, lables):
    AllText = {}
    labelList = []
    fileNameList = operationFile.getFileName(pathRoot)
    print("一共有" + str(len(fileNameList)) + "个vue文件")
    for label in lables:
        # 进入循环taglist清空
        labelList = []
        for fileName in fileNameList:
            # 获得文本
            html = operationFile.readHTML(fileName)
            datas = Context(LabelsCN).GetJsonText(label,html)
            for data in datas:
                labelList.append(data)
            # 再次去重
            labelList = list(set(labelList))
        jsonMap = formatText.getMap(labelList)
        # 将jsonMap装箱
        AllText[label] = jsonMap
    return AllText

def getScripts(pathRoot, scripts):
    AllText = {}
    scriptList = []
    fileNameList = operationFile.getFileName(pathRoot)
    print("一共有" + str(len(fileNameList)) + "个vue文件")
    for script in scripts:
        # 进入循环taglist清空
        scriptList = []
        for fileName in fileNameList:
            # 获得文本
            html = operationFile.readHTML(fileName)
            datas = operationText.getScript(html, script)
            for data in datas:
                scriptList.append(data)
            # 再次去重
            scriptList = list(set(scriptList))
        jsonMap = formatText.getMap(scriptList)
        # 将jsonMap装箱
        AllText[script] = jsonMap
    return AllText

大概,比如这些代码,其实很多代码都是一样的,核心代码就是datas = operationText.getScript(html, script),我要只能静态修改不同的类,无法动态的去调用不同的方法,但是我传入策略类,就可以解决这个问题。

策略模式的总结

策略模式属于行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

策略模式的优点

策略模式的缺点

策略模式的使用场景

在程序设计中,通常在以下几种情况中使用策略模式较多。

  1. 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
  2. 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可以将每个条件分支移入他们各自的策略类中以代替这些条件语句。
  3. 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
  4. 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
  5. 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。