• Python与Go中“类的归一化设计”实现与对比


    Python中类的归一化设计

    在Python中实现类的归一化设计有2种思路:一种是使用abc模块限制,另外一种是在父类种给某些方法主动抛出异常,如果子类不实现父类的方法,根据方法的调用顺序程序会报错。

    abc模块实现类的归一化

    import abc
    
    class Father(metaclass=abc.ABCMeta):
    
        @abc.abstractmethod
        def run(self):
            pass
        
    class Son(Father):
        pass
    
    if __name__ == '__main__':
        son = Son()

    抽象类Father在定义的时候指定其元类为abc.ABCMeta,里面的run方法用装饰器abc.abstractmethod装饰,这样所有继承它的子类就必须实现run这个方法。
    上述代码中Son类继承了Father类但是没有实现其父类的run方法,在Son类实例化对象的时候会抛出下列异常:

    TypeError: Can't instantiate abstract class Son with abstract methods run

    我们在子类中必须实现run方法才能使程序正常运行:

    import abc
    
    class Father(metaclass=abc.ABCMeta):
    
        @abc.abstractmethod
        def run(self):
            pass
        
    class Son(Father):
        def run(self):
            return 'run'
    
    if __name__ == '__main__':
        son = Son()

    异常处理的方式实现归一化效果

    利用abc模块实现的归一化设计是在子类实例化的时候进行判断的,这种方式难免有些强制,我平时比较常用异常处理的方式:

    class Father(object):
    
        def run(self):
            raise NotImplementedError("must have method run()")
    
    class Son(Father):
        pass
    
    if __name__ == '__main__':
        
        son = Son()
        son.run()

    从上面的代码可以看出:在实例化son对象的时候不会发生异常,但是在son试图调用run方法的时候,根据对象的属性与方法的查找顺序的原理可知,由于Son类本身没有实现run方法,只能从父类中去找,而父类的run方法会直接抛出异常!同样也实现了强制子类必须定义run方法的效果。

    Go语言实现“类归一化效果”1

    很多源码中会使用这种方式,使用下划线(_):

    package main
    
    type DataBaseInterface interface {
        Run1() string
        Run2() int
    }
    
    type TestStruct struct {
        Name string
        Age  int
    }
    
    func (t *TestStruct) Run1() string {
        return "Run1"
    }
    
    func (t *TestStruct) Run2() int {
        return 666
    }
    
    // TestStruct必须实现了DataBase接口才能编译过去 —— 确保结构体是实现了对应的interface
    var _ DataBaseInterface = &TestStruct{}
    
    func main() {
    
    }

    Go语言实现“类归一化效果”2

    在Go种其实并没有面向对象的概念,但是几乎所有面向对象的思想及设计我们都可以利用interface与struct去实现。

    明确几个概念

    首先,struct之间可以通过“组合”的方式实现类的“继承效果”。

    其次,所谓的实现了某个interface,其实就是实现了这个interface种定义的所有方法!—— 注意这里的 所有 很重要!

    实现代码

    接下来直接上代码:

    package main
    
    // 定义一个interface,里面有3个方法
    type TaskInterface interface {
        Run() int
        Task1() string
        Task2() string
    }
    
    // 父级struct,只实现了 TaskInterface 的2个方法 (当然也可以实现额外的方法)
    type BasicTask struct{
        Name string
    }
    // 实现interface的方法
    func (b *BasicTask) Task1() string{
        return "task1..."
    }
    func (b *BasicTask) Task2() string{
        return "task2..."
    }
    // 自己的方法
    func (b *BasicTask) BasicTask1() (int, string){
        return 666, "666"
    }
    
    
    // 子struct继承上面的基类
    type MyTask struct {
        BasicTask
    }
    
    // 子struct 必须实现 Run方法!!!否则下面的工厂函数会报错!!!
    func (m *MyTask) Run() int{
        return 222
    }
    
    // 定义一个工厂函数,返回TaskInterface(实际上返回的是子类实例化对象的结构体指针)
    // 注意这个工厂函数必须要求子struct Mytask必须实现Run()方法
    func MyTaskChild1() TaskInterface{

    // 注意在return之前,还可以实现一些其他的功能
    return &MyTask{ BasicTask{ Name: "MyTaskChild1"
    , }, } } func main() { }

    实现讲解

    1、首先我们定义了一个interface,这个interface有3个方法。

    2、结构体BasicTask,只实现了interface的其中2个方法,没有实现Run方法。

    3、子结构体MyTask继承了BasicTask,这样MyTask也相当于实现了interface的其中2个方法,但是也没有实现Run方法。

    4、最核心的就是那个工厂函数MyTaskCHild1,注意它在定义阶段返回的是我们之前定义的那个接口TaskInterface对象,而我们return的是子类Mytask的结构体指针。

    5、问题来了:如果MyTask这个结构体并没有实现接口TaskInterafce(如果没有实现Run方法),那么程序会报这样的错:

    6、这样就实现了“类归一化设计的效果”:继承了BasicTask的子类MyTask,必实现Run方法才能使程序正确运行。

  • 相关阅读:
    live2d 快速实现好看的看板娘特效
    JQuery 日期转换日期方法封装
    SQL Server 之 DateTime的常用方法
    C# 之DateDiff 时间差扩展方法
    SQL Server 之如何查询某数据库下的触发器和语句
    Css 设置固定表格头部,内容可滚动
    jquery 点击tr选中checkbox,解决checkbox的默认点击事件被阻止的问题
    VS切换代码自动补全模式
    C#实现软键盘的几个关键技术介绍
    C# 模拟软件键盘输入,使Winfrom窗体不获取鼠标焦点方法
  • 原文地址:https://www.cnblogs.com/paulwhw/p/14088704.html
Copyright © 2020-2023  润新知