前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面向对象困境之:横切关注点

面向对象困境之:横切关注点

作者头像
江不知
发布2019-12-19 15:40:15
1.3K0
发布2019-12-19 15:40:15
举报
文章被收录于专栏:编程拯救世界编程拯救世界

什么是关注点(Concern)?

A Concern is a term that refers to a part of the system divided on the basis of the functionality.

关注点是指基于功能划分系统的一部分。

什么是横切关注点(Crosscutting Concern)?

部分关注点「横切」程序代码中的数个模块,即在多个模块中都有出现,它们即被称作「横切关注点[1](Cross-cutting concerns, Horizontal concerns)」。

这样说好像还是特别抽象?那我们举个例子。

日志功能就是横切关注点的一个典型案例。日志功能往往横跨系统中的每个业务模块,即「横切」所有需要日志功能的类和方法体。所以我们说日志成为了横切整个系统对象结构的关注点 —— 也就叫做横切关注点啦。

面向对象的困境

对于 OOP 来说,我们很难在不同的继承关系链中共用代码。

例如 A 类继承 B 类,C 类继承 D 类,而 A 和 C 都需要实现一个写日志的方法。

代码语言:javascript
复制
class A(B):
    def write_log(self):
        pass

class C(D):
    def write_log(self):
        pass

当我们遇到这种情况时要怎么办呢?

解决方法

复制 - 粘贴

虽然我们对此嗤之以鼻,但在业务时间紧迫(或懒懒懒)时,很多人还是会采用这种方案。

方案的缺点一目了然:代码重复率高。

复制粘贴一时爽,后期维护火葬场。

引入基础类

让所有继承链都继承一个基础类 Base,然后在这个基础类中实现要共享的代码。

代码语言:javascript
复制
# 定义一个「超级」基础类,共享代码都在这里定义
class Base:
    def write_log(self):
        print("write log")
        pass

class B(Base):
    pass

class D(Base):
    pass

class A(B):
    pass

class C(D):
    pass

a = A()
a.write_log()

但随着时间的推移,这个基础类 Base 的规模会越来越大,职责也变得越来越模糊。

多重继承

像 PHP、Java、Swift 等是不支持多重继承的,Python 或 C++ 可以使用这种方案。

代码语言:javascript
复制
class Log:
    def write_log(self):
        print("write log")
        pass

class B():
    pass

class D():
    pass

# A 继承 B 也继承 Log
class A(B, Log):
    pass

class C(D, Log):
    pass

a = A()
a.write_log()

但多重继承会带来另一个问题:菱形缺陷[2](Diamond Problem)。

依赖注入

依赖注入[3]是指通过外部传入带有某个方法的对象来使用这个方法。

例如我们定义一个带有写日志方法的 Log 类,如果我们要在类 A 中调用这个方法,那么就把 Log 的一个对象直接注入类 A 中。

代码语言:javascript
复制
class Log:
    def write_log(self):
        print("write log")
        pass

class A():
    def __init__(self, log):
        self.log = log

    def write_a_log(self):
        self.log.write_log()

# 直接注入 Log 对象
a = A(Log())
a.write_a_log()

在 PHP 框架 Laravel[4] 中就大量使用了依赖注入。

面向协议编程

这里涉及到 Swift 中的协议协议扩展

先定义一个包含写日志方法 writeLog() 的协议:

代码语言:javascript
复制
// 定义协议
protocol Log {
    func writeLog()
}

然后为这个 writeLog() 提供一个默认的实现方法,即协议扩展

代码语言:javascript
复制
// 定义协议
protocol Log {
    func writeLog()
}

// 提供一个默认的实现方法
extension Log {
    func writeLog() {
        // coding
    }
}

这样一来,任何遵守 Log 协议的类都可以使用默认的 writeLog() 方法了。

参考资料

[1]

横切关注点: https://zh.wikipedia.org/wiki/%E6%A8%AA%E5%88%87%E5%85%B3%E6%B3%A8%E7%82%B9

[2]

菱形缺陷: https://www.quora.com/What-is-the-diamond-problem-in-programming

[3]

依赖注入: https://blog.tonyseek.com/post/notes-about-ioc-and-di/

[4]

Laravel: https://laravel.com/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程拯救世界 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是关注点(Concern)?
  • 什么是横切关注点(Crosscutting Concern)?
  • 面向对象的困境
  • 解决方法
    • 复制 - 粘贴
      • 引入基础类
        • 多重继承
          • 依赖注入
            • 面向协议编程
              • 参考资料
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档