preface
Of course, this idea is very important in Java, even almost all programming languages need, after all, the program has good scalability, maintainability who can not be rejected.
I recently came across some of the code I wrote when I first started writing Python, and the requirements implemented at that time had one obvious feature:
- Different objects have common behavior capabilities, but the specific implementation of each object is different.
In human language, merchants need to access the platform. The steps of access are the same, but the specific implementation is different.
As a “senior” Javaer, I wrote the implementation classes in great detail before I finished reading the requirements:
Of course, the final successful implementation of the requirements, and even the group has not written Java brother intimidate a leng leng, straight call cow force.
But he also made fun of me afterwards:
- Your design is good, but it feels so complicated that it takes several detour to find the real business logic (implementation classes) in the code.
By now, having written so much Python, I can sum up his feelings: Just not enough Pythonic.
Although Python does not have an Interface feature like Java, it does support inheritance as an object-oriented high-level language;
Here we can also take advantage of inherited features to implement interface-oriented programming:
class Car:
def run(self) :
pass
class Benz(Car) :
def run(self) :
print("benz run")
class BMW(Car) :
def run(self) :
print("bwm run")
def run(car) :
car.run()
if __name__ == "__main__":
benz = Benz()
bmw = BMW()
run(benz)
run(bmw)
Copy the code
The code is very simple, and there is no extends keyword in Python similar to the Java extends keyword. You simply include the base class in parentheses at the end of the class declaration.
In this way, the business logic can be implemented separately in each subclass for easy extension and maintenance.
Type checking
Because Python is a dynamically typed language, it is impossible to verify at compile time that a class fully implements all the methods of an interface, as Java does.
Python provides a solution to this problem: ABC (Abstract Base Classes), which we can approximate when we declare Base Classes as ABC:
import abc
class Car(abc.ABC) :
@abc.abstractmethod
def run(self) :
pass
class Benz(Car) :
def run(self) :
print("benz run")
class BMW(Car) :
pass
def run(car) :
car.run()
if __name__ == "__main__":
benz = Benz()
bmw = BMW()
run(benz)
run(bmw)
Copy the code
Whenever a class does not implement a method, the runtime throws an exception:
bmw = BMW()
TypeError: Can't instantiate abstract class BMW with abstract methods run
Copy the code
While you can’t do this before you run (you don’t need to compile, after all), something is better than nothing.
The duck type
Both of these approaches may seem like elegant implementations of interface oriented programming after all, but they are not really Pythonic enough.
Before we go on what is the nature of interfaces?
In a static language like Java, interface oriented programming is cumbersome, often referred to as subclass to parent, and therefore requires additional code.
The benefits are also obvious, requiring only the parent class to run.
But we don’t need to be too attached to Interface, it is a protocol, specification, does not specifically refer to the Java Interface, and some languages do not even have this keyword.
The dynamic language feature also does not require mandatory verification that methods are implemented.
In Python we can use the duck type for elegant interface oriented programming.
Before we do that, let’s take a look at duck types, as wikipedia says:
- “A bird can be called a duck when it is seen to walk like a duck, swim like a duck and quack like a duck.”
My translation in plain English is:
Even if two classes don’t want to do anything at all, if they both implement the same method, they can be used as the same type of class.
Here’s a simple example:
class Order:
def create(self) :
pass
class User:
def create(self) :
pass
def create(obj) :
obj.create()
if __name__ == "__main__":
order = Order()
user = User()
create(order)
create(user)
Copy the code
The order and user are not related at all, except that they both have the same method and, thanks to dynamic languages’ inability to validate types, can be run as if they are of the same type.
So based on the duck type, we can simplify the previous code a bit:
class Car:
def run(self) :
pass
class Benz:
def run(self) :
print("benz run")
class BMW:
def run(self) :
print("bwm run")
def run(car) :
car.run()
if __name__ == "__main__":
benz = Benz()
bmw = BMW()
run(benz)
run(bmw)
Copy the code
Because in the duck type we care about the behavior, not the type; So it’s possible to do interface-oriented programming without inheritance.
conclusion
I think people who don’t normally work with dynamically typed languages will find something new when they learn about them, just like Python veterans who first started using Java; I find the syntax verbose, but I also admire features like type checking and parameter validation.
The debate of movement and language is not discussed here, each has his own good, good shoes to wear only oneself know.
By the way, it’s not just dynamic languages that have the duck type. Some static languages can also play this trick.