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方法才能使程序正确运行。