C
Carl Banks
While thinking about generational garbage collection, the thought of
generational interfaces occurred to me. I'd thought I'd run it by you
guys. I'm curious if there are any examples of this out there.
I've opined on this chat room before that interfaces are more often
cumbersome than helpful, especially in the early stages of a project
where lots of refactoring is (or ought to be) happening. But as
projects mature, interfaces do too, and that made me think interfaces
could be generated automatically by monitoring the software over time.
As an example, say I'm writing a flight simulator, and I have a
abstract base class Airplane, that I want to have an interface
someday, but I don't know what it is yet. So I define an
AirplaneInterface = InterfaceTracker("Airplane")
What this does is to open a sort of persistent database called
Airplane (details aren't important now). The database tracks all
objects that claim to implement the interface.
So say the first airplane is a Piper Cherokee. I'd write a class like
this:
class PiperCherokeeX1(object):
wingspan = 52.2
wingchord = 7.9
...
def __init__(self):
self.x = 0.0
self.y = 0.0
self.z = 0.0
...
set_up_initial_state()
...
AirplaneInterface.report(self)
def move_stick(self,dx,dy):
...
def move_thottle(self,ds):
...
def move_rudder_pedals(self,ddr):
...
def camera_matrix(self):
return self._quat_to_matrix(self.q0,self.q1,self.q2,self.q3)
def step(self,dt):
...
The key here is the call to AirplaneInterface.report() at the end of
__init__; this tells the interface tracker that, as of this call, this
object is implementing the Aircraft interface.
At this point, the interface tracker notes that PiperCherokeeX1 object
has certain methods (move_stick, move_throttle, etc), certain class
attributes, and certain instance attributes. And that's all it does--
at first. It just writes information into the database.
As time passes, and development continues, methods and data are added,
changed, reconfigured. For instance, I might split up move_stick()
into move_stick_x() and move_stick_y() for some reason. Then I might
get rid of these functions altogether in favor of a
move_control(self,n,dx). And so on. I add more classes that
implement the Aircraft interface, too. They look almost nothing like
the original interface.
However, through all that, the class attribute "wingspan" remains
there. Until one day when the project is quite mature I add a new
class, say Airbus380, that fails to define "wingspan". When this
class calls AirplaneInterface.report(), it raises an
InterfaceException.
Basically, the InterfaceTracker decides, after some threshold of
nearly universal usage of a certain method or attribute, that it has
become a required part of the interface and starts raising exceptions
when it's not there.
Make sense? Details can vary, but that's the basic idea. In this
way, you can combine some of the openness that helps in early
development, but also have some of the benefits of stricter typing
when things mature and turn out to be pretty strictly useful, without
much effort.
Thoughts? (Surely someone's thought to do this before.)
Carl Banks
generational interfaces occurred to me. I'd thought I'd run it by you
guys. I'm curious if there are any examples of this out there.
I've opined on this chat room before that interfaces are more often
cumbersome than helpful, especially in the early stages of a project
where lots of refactoring is (or ought to be) happening. But as
projects mature, interfaces do too, and that made me think interfaces
could be generated automatically by monitoring the software over time.
As an example, say I'm writing a flight simulator, and I have a
abstract base class Airplane, that I want to have an interface
someday, but I don't know what it is yet. So I define an
AirplaneInterface = InterfaceTracker("Airplane")
What this does is to open a sort of persistent database called
Airplane (details aren't important now). The database tracks all
objects that claim to implement the interface.
So say the first airplane is a Piper Cherokee. I'd write a class like
this:
class PiperCherokeeX1(object):
wingspan = 52.2
wingchord = 7.9
...
def __init__(self):
self.x = 0.0
self.y = 0.0
self.z = 0.0
...
set_up_initial_state()
...
AirplaneInterface.report(self)
def move_stick(self,dx,dy):
...
def move_thottle(self,ds):
...
def move_rudder_pedals(self,ddr):
...
def camera_matrix(self):
return self._quat_to_matrix(self.q0,self.q1,self.q2,self.q3)
def step(self,dt):
...
The key here is the call to AirplaneInterface.report() at the end of
__init__; this tells the interface tracker that, as of this call, this
object is implementing the Aircraft interface.
At this point, the interface tracker notes that PiperCherokeeX1 object
has certain methods (move_stick, move_throttle, etc), certain class
attributes, and certain instance attributes. And that's all it does--
at first. It just writes information into the database.
As time passes, and development continues, methods and data are added,
changed, reconfigured. For instance, I might split up move_stick()
into move_stick_x() and move_stick_y() for some reason. Then I might
get rid of these functions altogether in favor of a
move_control(self,n,dx). And so on. I add more classes that
implement the Aircraft interface, too. They look almost nothing like
the original interface.
However, through all that, the class attribute "wingspan" remains
there. Until one day when the project is quite mature I add a new
class, say Airbus380, that fails to define "wingspan". When this
class calls AirplaneInterface.report(), it raises an
InterfaceException.
Basically, the InterfaceTracker decides, after some threshold of
nearly universal usage of a certain method or attribute, that it has
become a required part of the interface and starts raising exceptions
when it's not there.
Make sense? Details can vary, but that's the basic idea. In this
way, you can combine some of the openness that helps in early
development, but also have some of the benefits of stricter typing
when things mature and turn out to be pretty strictly useful, without
much effort.
Thoughts? (Surely someone's thought to do this before.)
Carl Banks