Component Architecture¶
Guillotina is built on a component architecture. The component architecture uses adapter and singleton software design patterns to help manage complexity. It allows users to register and lookup adapters and utility defined against interfaces.
Why¶
program to an interface, not an implementation
favor object composition over class inheritance
keep objects stupid
The component architecture is a powerful tool to help you build complex software that needs to be extensible. In software engineering, adapters and singletons are used often so it is a natural pattern to build on.
Almost any component/functionality in Guillotina can be overridden in an add-on application by overriding Guillotina’s components.
Basics¶
To query an adapter on content:
from guillotina.component import get_adapter
from guillotina.interfaces import IResource
from zope.interface import Interface
from guillotina import configure
class IMyAdapter(Interface):
pass
@configure.adapter(for_=IResource, provides=IMyAdapter)
class MyAdapter:
def __init__(self, ob):
self.ob = ob
def do_something(self):
pass
adapter = get_adapter(ob, IMyAdapter)
adapter.do_something()
To query for a utility(which is what we call singletons), it’s similiar:
from guillotina.component import get_utility
from guillotina.interfaces import IPermission
permission = get_utility(IPermission, name='guillotina.AccessContent')
Details¶
To describe power of this approach, we’ll go through using adapters without the registration and lookup and then with the component architecture.
Adapters without CA¶
class Automobile:
wheels = 4
def __init__(self):
pass
class Motorcycle(Automobile):
wheels = 2
class SemiTruck(Automobile):
wheels = 18
class Operate:
def __init__(self, automobile: Automobile):
self.automobile = automobile
def drive(self):
pass
class OperateMotocycle:
def drive(self):
pass
class OperateSemi:
def drive(self):
pass
Then, to use these adapters, you might do something like:
if isinstance(auto, SemiTruck):
operate = OperateSemi(auto)
elif isinstance(auto, Motorcycle):
operate = OperateMotocycle(auto)
else:
operate = Operate(auto)
operate.drive()
Adapters with CA¶
from guillotina import configure
from zope.interface import Attribute, Interface, implementer
class IAutomobile(Interface):
wheels = Attribute('number of wheels')
class IMotocycle(IAutomobile):
pass
class ISemiTruck(IAutomobile):
pass
@implementer(IAutomobile)
class Automobile:
wheels = 4
@implementer(IMotocycle)
class Motorcycle(Automobile):
wheels = 2
@implementer(ISemiTruck)
class SemiTruck(Automobile):
wheels = 18
class IOperate(Interface):
def drive():
pass
@configure.adapter(for_=IAutomobile, provides=IOperate)
class Operate:
def __init__(self, automobile: Automobile):
self.automobile = automobile
def drive(self):
return 'driving automobile'
@configure.adapter(for_=IMotocycle, provides=IOperate)
class OperateMotocycle(Operate):
def drive(self):
return 'driving motocycle'
@configure.adapter(for_=ISemiTruck, provides=IOperate)
class OperateSemi(Operate):
def drive(self):
return 'driving semi'
Then, to use it:
from guillotina.component import get_adapter
semi = SemiTruck()
operate = get_adapter(semi, IOperate)
operate.drive()