• 开放封闭原则


    开放封闭原则

    面向对象原则

    在面向对象的设计中有很多流行的思想,比如"所有的成员变量都应该设置为私有(python中使用__定义私有属性)","要避免使用全局变量"等等。这些思想的源泉就来自于面向对象的六大原则,分别为单一职责原则、开放封闭原则、依赖倒置原则、接口隔离原则、里氏替换原则、迪米特法则。
    

    开放封闭原则描述

    符合开放封闭原则的模块都有两个主要特性:
    	1.面向扩展开放(Open For Extension)
    	也就是说模块的行为是能够被扩展的。当应用程序的需求变化时,我们可以使模块表现出全新的或与以往不同的行为,以满足新的需求。
    	2.面向修改封闭(Closed For Modification)
    	模块的源代码是不能被侵犯的,任何人都不允许修改已有源代码。
    

    看起来上述两个特性是互相冲突的,因为通常扩展模块行为的常规方式就是修改该模块。一个不能被修改的模块通常被认为其拥有着固定的行为。那么如何是这两个相反的特性共存?就是使用抽象

    本人也是在学习java的过程中认识到抽象的概念,java是面向对象最典型的语言,java崇尚面向接口编程,而这个接口其实就是一个抽象出来的类,其中拥有很多抽象方法,(为什么布面向抽象类编程,可能是因为java只能单继承,但是可以实现多个接口),而python中没有抽象的具体概念,当然也是有实现抽象的办法。

    1.方式一:抛出NotImplementedError异常

    class SuperClass(object):
        def eat(self):
            print('父类吃')
        
        def display(self):
            raise NotImplementedError('子类必须实现或重写该方法')
    
    class SubClass(SuperClass):
        def display(self):
            print('子类的display方法')
    

    2.使用元类(metaclass,元类的概念就非常抽象)

    python3版本

    from abc import ABCMeta,abstractmethod
    class SuperClass(metaclass=ABCMeta):
        def eat(self):
            print('父类吃')
            
        @abstractmethod   
        def display(self):
            pass
       
    class SubClass(SuperClass):
        def display(self):
            print(子类必须实现的display方法)
    

    python2版本

    from abc import ABCMeta,abstractmethod
    class SuperClass:
        __metaclass__ = ABCMeta
        
        def eat(self):
            print('父类吃')
            
        @abstractmethod   
        def display(self):
            pass
       
    class SubClass(SuperClass):
        def display(self):
            print(子类必须实现的display方法)
    

    生活中简单的抽象:

    自然界中很许多的动物,他们都有各自的特性,但是也有很多的共性,我们将共性抽取出来,生成一个Animal的抽象类,其中具有抽象方法eat(),run()等这些所有动物都具有的共性,这个类就作为其他所有动物的超类,而他们都必须在继承该类后重写这些抽象方法,Animal类不会再去修改,也就是面向修改封闭,而继承他的子类会根据自己的特性在进行扩展,这就是面向扩展开放。
    

    Django中的开放封闭

    其实我们在使用Django的时候也不难发现其中运用到的开放封闭原则,最好的案例就是Django的settings文件。

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    

    这是settings文件中配置中间件的部分,我们不需要使用某个中间件时,只需要将其注释,而不是去删除该中间件模块,我们自定义中间件,只需要在MIDDLEWARE列表的合适位置添加自定义中间件的路径就可以了,这就是在不改变源代码的情况下实现了功能的扩展。

    在Flask中实现类似功能

    说明:发送消息的方式有很多种,如邮件,短信推送,微信推送等,所以我们模仿Django的MIDDLEWARE也来实现以下发送信息的模块。

    settings.py

    MSG_LIST = [
        'message.msg.Msg',
        'message.email.Email',
    ]
    

    message下的base.py(抽象类)

    class Base(object):
        def send(self,msg):
            raise NotImplementedError('... ')
    

    message下的msg.py

    from .base import Base
    class Msg(Base):
        def __init__(self):
            self.username = 'ss'
            self.pwd = 'aksjf'
    
        def send(self):
            print('msg_send')
    

    message下的email.py

    from .base import Base
    class Email(Base):
        def __init__(self):
            self.username = 'ss'
            self.pwd = 'aksjf'
    
        def send(self):
            print('email_send')
    

    我们在message模块的__init___下进行编写

    import settings
    import importlib
    
    
    def send_msg(msg):
        for item in settings.MSG_LIST:
            m, c = item.rsplit('.', maxsplit=1)
            md = importlib.import_module(m)
            cls = getattr(md, c)()
            cls.send()
    

    app中使用

    from flask import Flask
    from message import send_msg
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        # 比如这里需要使用到发送消息
       	send_msg('some message')
        return 'index 页面'
    

    如果我们只需要使用短信推送,我们就可以将settings文件中的message.email.Email注释,如果有添加微信推送功能,我们也只需要在添加一行文件路径就可以了,当然这只是个案例,也许不符合现实生活的需要。

    总结

    要明白程序是不可能100%完全封闭的,无论模块的设计有多封闭,总是有各种各样的变化来打破这种封闭,所以需要讲究策略来实现封闭。
    
  • 相关阅读:
    Go 只读/只写channel
    MongoDB 倾向于将数据都放在一个 Collection 下吗?
    Go语言string,int,int64 ,float之间类型转换方法
    [转]流程自动化机器人(RPA)概念、原理与实践
    ESXi以及WorkStation缩减thin provision模式Linux虚拟机磁盘的方法
    Linux 安装宋体字体的简单办法
    浏览器性能简单测试
    学习面试题Day04
    学习面试题Day05
    学习面试题Day06
  • 原文地址:https://www.cnblogs.com/louyefeng/p/10925652.html
Copyright © 2020-2023  润新知