Architecture Orientée Service¶
Gaphor a une architecture orientée services. Qu’est-ce que cela signifie ? Gaphor est construit comme un ensemble de petites îles (services). Chaque îlot fournit une fonctionnalité spécifique. Par exemple, nous utilisons des services distincts pour charger/sauvegarder les modèles, pour fournir la structure du menu, et pour gérer le système d’annulation.
Dans le fichier pyproject.toml, nous définissons les services comme des points d’entrée. Grâce à ces points d’entrée, les applications peuvent enregistrer des fonctionnalités destinées à des usages spécifiques. Nous regroupons également ces points d’entrée en groupes de points d’entrée. Par exemple, nous utilisons le groupe de points d’entrée console_scripts pour lancer une application depuis la ligne de commande.
Services¶
Gaphor est modélisé autour du concept de services. Chaque service peut être enregistré auprès de l’application et peut ensuite être utilisé par d’autres services ou d’autres objets présents dans l’application.
Chaque service doit implémenter l’interface Service. Cette interface définit une méthode :
shutdown(self)
C’est ce qu’on appelle lorsqu’un service doit être nettoyé.
Nous permettons à chaque service de définir ses propres méthodes, à condition que le service soit également implémenté.
Les services doivent être définis comme des points d’entrée dans le fichier pyproject.toml.
En général, un service effectue un travail en arrière-plan. Les services peuvent également exposer des actions qui peuvent être invoquées par les utilisateurs. Par exemple, la combinaison de touches Ctrl-z (annuler) est implémentée par le service UndoManager.
Un service peut également dépendre d’autres services. L’initialisation du service résout ces dépendances. Pour définir une dépendance de service, il suffit de l’ajouter au constructeur par son nom défini dans le point d’entrée :
class MyService(Service):
def __init__(self, event_manager, element_factory):
self.event_manager = event_manager
self.element_factory = element_factory
event_manager.subscribe(self._element_changed)
def shutdown(self):
self.event_manager.unsubscribe(self._element_changed)
@event_handler(ElementChanged)
def _element_changed(self, event):
Les services qui proposent des actions doivent également hériter de l’interface ActionProvider. Cette interface ne nécessite pas l’implémentation de méthodes supplémentaires. Les méthodes d’action doivent être annotées avec l’annotation @action.
Exemple : ElementFactory¶
Un bon exemple de service utilisé est le ElementFactory. C’est l’un des services de base.
Lorsqu’un événement important se produit, comme la création ou la destruction d’un élément, l’événement correspondant est émis. Nous utilisons alors un gestionnaire d’événement pour la classe ElementFactory qui stocke les signaux d’ajout/suppression dans le système d’annulation. Un autre exemple d’événements émis concerne les Element. Ces classes, ou plus précisément leurs propriétés, envoient des notifications à chaque fois que leur état change.
Points d’entrée (Entry Points)¶
Gaphor utilise un groupe principal :refentry point <https://packaging.python.org/en/latest/specifications/entry-points/> appelé gaphor.services.
Les services sont utilisés pour réaliser la fonctionnalité de base de l’application tout en décomposant les fonctions en composants individuels. Par exemple, la fabrique d’éléments et le gestionnaire d’annulation sont tous deux des services.
Des plugins peuvent également être créés pour étendre Gaphor au-delà de ses fonctionnalités de base. Par exemple, un plugin peut être créé pour connecter les données du modèle à d’autres applications. Les plugins sont également définis comme des services. Par exemple, un nouveau plugin d’exportation XMI serait défini comme suit dans le fichier pyproject.toml :
[tool.poetry.plugins."gaphor.services"]
"xmi_export" = "gaphor.plugins.xmiexport:XMIExport"
Interfaces¶
Chaque service (et plugin) doit implémenter l’interface gaphor.abc.Service :
Un autre service plus spécialisé, qui hérite également de gaphor.abc.Service, est le service UI Component. Les services qui utilisent cette interface servent à définir les fenêtres et les fonctionnalités de l’interface utilisateur. Un composant d’interface utilisateur doit implémenter l’interface gaphor.ui.abc.UIComponent :
- class gaphor.ui.abc.UIComponent[source]¶
Un composant de l’interface utilisateur.
- abstractmethod close()[source]¶
Fermez le composant de l’interface utilisateur.
Le composant peut décider de cacher ou de détruire les composants de l’interface utilisateur.
Typiquement, un service et un composant d’interface utilisateur (UI) souhaitent proposer certaines actions à l’utilisateur à l’aide d’entrées de menu. Chaque service et composant peut annoncer des actions en implémentant l’interface gaphor.abc.ActionProvider :