Diferencia entre revisiones de «Desarrollo de frameworks»

De Wiki de EGC
Saltar a: navegación, buscar
(Preparación para práctica sobre creación de frameworks)
 
(No se muestran 10 ediciones intermedias del mismo usuario)
Línea 11: Línea 11:
  
 
En esta práctica recrearemos un desarrollo de un framework para el analisis de modelos de variabilidad.
 
En esta práctica recrearemos un desarrollo de un framework para el analisis de modelos de variabilidad.
 +
 +
Podréis encontrar el código en github [https://github.com/EGCETSII/framework aquí]
 +
 +
Presentación en pdf [https://hdvirtual.us.es/discovirt/index.php/s/N54RrWogNwQkPAb aquí]
 +
 +
Clase grabada [https://hdvirtual.us.es/discovirt/index.php/s/k7G3cpBdyNc4cY5 aquí]
  
 
=Ejercicio=
 
=Ejercicio=
Línea 17: Línea 23:
 
-----------------------
 
-----------------------
  
* Análisis
+
* Análisis: Identificamos los frozen points de nuestro framework
Identificamos los frozen points de nuestro framework
+
** Clases del dominio: El dominio sobre el que desarrollaremos nuestro framework es el del analisis de modelos de características. Para ello, comenzaremos por conocer los elementos básicos de los mismos según lo visto en teoría.
 +
** Gestión de plugins: También analizaremos que partes del framework permitirán extensivilidad basandonos en plugins.
 +
 
 +
* Diseño: En este apartado crearemos una clase Feature y una clase Relación para definir los conceptos con los que trabajará el framework
 +
fichero: core/frozen_points_domain.py
 +
<syntaxhighlight lang="python" line='line'>
 +
from typing import Sequence
 +
#Este modulo añade soporte a clases abstractas
 +
from abc import ABC, abstractmethod
 +
 
 +
#Esto es la definición de la clase Relación
 +
class Relation:
 +
 
 +
    #Esto es un constructor en python
 +
    #Cuando colocamos el nombre de la clase entre comillas es la forma de usar tipado fuerte en python.
 +
    #De cara a crear un framework de caja blanca, necesitamos orientación a objetos y tipado de objectos
 +
    #Esto está disponible en python desde la versión 3.8 en adelante
 +
    def __init__(self, parent: 'Feature', children: Sequence['Feature'], card_min: int, card_max: int):
 +
        self.parent = parent
 +
        self.children = children
 +
        self.card_min = card_min
 +
        self.card_max = card_max
 +
 
 +
    #Este método añade un elemento a la colección de hijos de una relación
 +
    def add_child(self, feature: 'Feature'):
 +
        self.children.append(feature)
 +
 
 +
    #Este método verifica la cardinalidad de la relación y el número de hijos para devolver true si es mandatory
 +
    def is_mandatory(self) -> bool:
 +
        return (len(self.children) == 1 and self.card_max == 1 and self.card_min == 1)
 +
 
 +
    #Este método verifica la cardinalidad de la relación y el número de hijos para devolver true si es optional
 +
    def is_optional(self) -> bool:
 +
        return (len(self.children) == 1 and self.card_max == 1 and self.card_min == 0)
 +
 
 +
    #Este método verifica la cardinalidad de la relación y el número de hijos para devolver true si es or
 +
    def is_or(self) -> bool:
 +
        return (len(self.children) > 1 and self.card_max == len(self.children) and self.card_min == 1)
 +
 
 +
    #Este método verifica la cardinalidad de la relación y el número de hijos para devolver true si es alternative
 +
    def is_alternative(self) -> bool:
 +
        return (len(self.children) > 1 and self.card_max == 1 and self.card_min ==  1)
 +
 
 +
    #Este método to string en python
 +
    def __str__(self):
 +
        res = self.parent.name + '[' + str(self.card_min) + ',' + str(self.card_max) + ']'
 +
        for _child in self.children:
 +
            res += _child.name + ' '
 +
        return res
 +
 
 +
#Esto es la definición de la clase Feature
 +
class Feature:
 +
 
 +
    #Este es el método constructor. Aqui definimos que queremos que cada característica tenga un nombre y un conjunto de relaciones.
 +
    def __init__(self, name: str, relations: Sequence['Relation']):
 +
        self.name = name
 +
        self.relations = relations
 +
 
 +
    #Este es un metodo auxiliar para añadir una relación a una característica
 +
    def add_relation(self, relation: 'Relation'):
 +
        self.relations.append(relation)
 +
 
 +
    #Este método nos devuelve el conjunto de relaciones de una característica
 +
    def get_relations(self):
 +
        return self.relations
 +
 
 +
    #Este método to string en python
 +
    def __str__(self):
 +
        return self.name
 +
 
 +
#Esta clase representa a un modelo de características
 +
class FeatureModel():
 +
 
 +
    #En el constructor solo encontramos una feature raiz
 +
    def __init__(self, root: Feature):
 +
        self.root = root
 +
 
 +
    #Este método devuelve el conjunto de relaciones existentes en el modelo
 +
    def get_relations(self, feature=None):
 +
        relations = []
 +
        if not feature:
 +
            feature = self.root
 +
        for relation in feature.relations:
 +
            relations.append(relation)
 +
            for _feature in relation.children:
 +
                relations.extend(self.get_relations(_feature))
 +
        return relations
 +
 
 +
    #Este método devuelve el conjunto de features de un modelo
 +
    def get_features(self):
 +
        features = []
 +
        features.append(self.root)
 +
        for relation in self.get_relations():
 +
            features.extend(relation.children)
 +
        return features
 +
 
 +
    #Este método devuelve la feature identificandola por el nombre (TODO: Implementar con __equals__)
 +
    def get_feature_by_name(self, str) -> Feature:
 +
        features = self.get_features
 +
        for feat in features:
 +
            if feat.name == str:
 +
                return feat
 +
        raise ElementNotFoundException
  
** Clases del dominio
+
    #Este método to string en python
El dominio sobre el que desarrollaremos nuestro framework es el del analisis de modelos de características.  
+
    def __str__(self) -> str:
Para ello, comenzaremos por conocer los elementos básicos de los mismos según lo visto en teoría.  
+
        res = 'root: ' + self.root.name + '\r\n'
 +
        for i, relation in enumerate(self.get_relations()):
 +
            res += f'relation {i}: {relation}\r\n'
 +
        return(res)
  
** Gestión de plugins
+
#This is an abstract Operation. To define abstract methos we rely on ABC module of the core python distribution
También analizaremos que partes del framework permitirán extensivilidad basandonos en plugins.  
+
class Operation(ABC):
Para
 
  
**Diseño
+
    #This abstract method, executes an operation given a feature model
En este apartado crearemos una clase Feature y una clase Relación para definir los conceptos con los que trabajará el framework
+
    @abstractmethod
 +
    def execute(self, model: FeatureModel) -> 'Operation':
 +
        pass
 +
</syntaxhighlight>
  
<script>
+
También implementamos el sistema dentro del core para la carga de plugins
Aqui aparecerá el código
+
fichero: core/frozen_points_plugins.py
</script>
+
<syntaxhighlight lang="python" line='line'>
 +
from types import FunctionType, ModuleType
 +
from typing import List
  
También implementamos un primer plugin y el sistema dentro del core para la carga de pluggins
+
# Este modulo nos permite obligar ciertas listas con tipos
 +
from collections import UserList
 +
# Estos modulos nos permiten explorar las clases existentes en distintos plugins 'a la java reflexion'
 +
from importlib import import_module
 +
from pkgutil import iter_modules
 +
import inspect
  
<script>
+
# Importamos las clases del core que vamos a necesitar (blackbox)
Aqui aparecerá el código
+
from core.frozen_points_domain import Operation
</script>
+
from core.frozen_points_domain import FeatureModel
  
**Instanciación
+
# Definimos las posibles rutas a los plugins
Finalmente implementaremos una aplicación que consuma tanto nuestros módulos del core como el plugin creado.
+
PLUGIN_PATHS = [
<script>
+
    'plugin',
Aqui aparecerá el código
+
]
</script>
 
  
Iteración 2
+
# Definimos una coleccion para almacenar los objetos de tipo operacion.
-----------------------
+
# Es interesante que las colecciones tipadas funcionan como clases y pueden tener métodos
 +
class Operations(UserList):  # pylint: disable=too-many-ancestors
 +
    data: List[Operation]
 +
 
 +
    def search_by_name(self, name: str) -> Operation:
 +
        candidates = filter(lambda op: op.__name__ == name, self.data)
 +
        try:
 +
            operation = next(candidates, None)
 +
        except StopIteration:
 +
            raise OperationNotFound
 +
        return operation
 +
 
 +
# Definimos una clase que represente a un plugin dentro del framwork.
 +
# Notese que también contiene los modulos y las operaciones que haya en la instalación de python
 +
class Plugin:
 +
    def __init__(self, module: ModuleType) -> None:
 +
        self.module: ModuleType = module
 +
        self.operations: Operations = Operations()
 +
 
 +
    @property
 +
    def name(self):
 +
        return self.module.__name__.split('.')[-1]
 +
   
 +
    # Esta clase añade las operaciones de un modulo
 +
    def append_operation(self, operation: Operation) -> None:
 +
        self.operations.append(operation)
 +
 
 +
    def use_operation(self, name: str, src: FeatureModel) -> Operation:
 +
        operation = self.operations.search_by_name(name)
 +
        return operation().execute(model=src)
 +
 
 +
# Definimos una lista de plugins y algunos métodos importantes
 +
class Plugins(UserList):  # pylint: disable=too-many-ancestors
 +
    data: List[Plugin]
 +
 
 +
    # buscamos los plugins que se llamen como queremos
 +
    def get_plugin_by_name(self, name: str):
 +
        for plugin in self.data:
 +
            if plugin.name == name:
 +
                return plugin
 +
        raise PluginNotFound
 +
 
 +
    # devolvemos la lista de plugins           
 +
    def get_plugin_names(self) -> List[str]:
 +
        return [plugin.name for plugin in self.data]
 +
 
 +
    #devolvemos las operacione sde un plugin
 +
    def get_operations_by_plugin_name(self, plugin_name: str) -> Operations:
 +
        try:
 +
            plugin = self.get_plugin_by_name(plugin_name)
 +
            return plugin.operations
 +
        except PluginNotFound:
 +
            return Operations()
 +
 
 +
#Esta es una clase especial que nos va a permitir enumerar y ejecutar las operaciones que tengamos dentro de los plugins
 +
class DiscoverMetamodels:
 +
   
 +
    #Cuando creamos la clase, esta inicializa la lista de modulos existentes y llama al discover para buscar plugins y modulos
 +
    def __init__(self):
 +
        self.module_paths: List[ModuleType] = list()
 +
        for path in PLUGIN_PATHS:
 +
            try:
 +
                module: ModuleType = import_module(path)
 +
                self.module_paths.append(module)
 +
            except ModuleNotFoundError:
 +
                print('ModuleNotFoundError %s', path)
 +
        self.plugins: Plugins = self.discover()
 +
 
 +
    #Este metodo busca las clases existentes en los módulos encontrados
 +
    def search_classes(self, module):
 +
        classes = []
 +
        for _, file_name, ispkg in iter_modules( module.__path__, module.__name__ + '.' ):
 +
            if ispkg:
 +
                classes += self.search_classes(import_module(file_name))
 +
            else:
 +
                _file = import_module(file_name)
 +
                classes += inspect.getmembers(_file, inspect.isclass)
 +
        return classes
 +
 
 +
    # Este método se encarga de buscar los plugins modulos y clases existentes en el path indicado como variable global
 +
    def discover(self) -> dict:
 +
        plugins = Plugins()
 +
        for pkg in self.module_paths:
 +
            for _, plugin_name, ispkg in iter_modules(pkg.__path__, pkg.__name__ + '.'):
 +
                if not ispkg:
 +
                    continue
 +
                module = import_module(plugin_name)
 +
                plugin = Plugin(module=module)
 +
                classes = self.search_classes(module)
 +
                for _, _class in classes:
 +
                    if not _class.__module__.startswith(module.__package__):
 +
                        continue  # Exclude modules not in current package
 +
                    #!! Fijaos como añadimos a la colección de operaciones cuando la clase operaciones hereda de la clase abstracta !!
 +
                    inherit = _class.mro()
 +
                    if Operation in inherit:
 +
                        plugin.append_operation(_class) 
 +
                plugins.append(plugin)
 +
        return plugins
 +
</syntaxhighlight>
 +
 
 +
Finalmente implementaremos un plugin
 +
fichero: plugin/count_leafs/count_leafs_op.py
 +
<syntaxhighlight lang="python" line='line'>
 +
from core.frozen_points_domain import Operation
 +
from core.frozen_points_domain import FeatureModel
 +
 
 +
class CountLeafs(Operation):
 +
 
 +
    def execute(self, model: FeatureModel) -> 'Operation':
 +
        result = 0
 +
        for feat in model.get_features():
 +
            if len(feat.get_relations())==0:
 +
                result= result +1
 +
        return result
 +
</syntaxhighlight>
 +
 
 +
 
 +
* Instanciación: Finalmente implementaremos una aplicación que consuma tanto nuestros módulos del core como el plugin creado.
 +
fichero:./main.py
 +
<syntaxhighlight lang="python" line='line'>
 +
 
 +
from core.frozen_points_domain import *
 +
from core.frozen_points_plugins import DiscoverMetamodels
 +
 
 +
# Creamos el manager y lo inicializamos
 +
dm = DiscoverMetamodels()
 +
#Buscamos los plugins disponibles
 +
available_plugins = dm.discover()
 +
 
 +
#Creamos un modelo de manera programatica
 +
feature_b = Feature('B', [])
 +
relation = Relation(parent=None, children=[feature_b], card_min=0, card_max=1)
 +
feature_a = Feature('A', [relation])
 +
relation.parent = feature_a
 +
fm = FeatureModel(feature_a)
 +
 
 +
#Imprimimos ese modelo
 +
print(fm)
 +
 
 +
#Imprimimos los plugins disponibles
 +
print(available_plugins.get_plugin_names())
  
* Análisis
+
#Buscamos el plugin que acabamos de crear
Identificamos los frozen points de nuestro framework al solicitarnos la implementación de un sistema de lectura de los modelos en json.
+
plugin=available_plugins.get_plugin_by_name('count_leafs')
  
** Clases del dominio
+
#Imprimimos las operaciones de los plugins
Añaadiremos las clases necesarias para implementar distintos tipos de serializadores de ficheros.  
+
print(plugin.operations)
  
 +
#Usamos la operacion CountLeafs
 +
result=plugin.use_operation('CountLeafs',fm)
  
**Diseño
+
#Imprimimos el resultado
En este apartado crearemos una clase Reader en el core
+
print("El modelo tiene " + result + "Features hojas")
<script>
+
</syntaxhighlight>
Aqui aparecerá el código
 
</script>
 
  
También implementamos extenderemos el soporte para añadir readers mediante plugins
+
--IMPORTANTE--
 +
Añadir los ficheros __init__.py para que las carpetas core y core_leafs sean modulos Python
  
<script>
+
Iteración 2
Aqui aparecerá el código
+
-----------------------
</script>
+
Se ha introducido un nuevo requisito para un nuevo producto en el que se nos pide una operación que cuente el número de productos del modelo. TIEMPO 20 minutos
  
**Instanciación
+
* Análisis: Identificamos los frozen points de nuestro framework al solicitarnos la implementación de una operación que nos diga cuantas características tiene un modelo
Finalmente implementaremos una aplicación que consuma tanto nuestros módulos del core como el nuevo plugin creado.  
+
** Clases del dominio: Añadiremos las clases necesarias para implementar distintos tipos de serializadores de ficheros.
<script>
+
* Diseño: En este apartado crearemos un nuevo plugin que implemente esa operación
Aqui aparecerá el código
+
* Instanciación: Finalmente implementaremos una aplicación que consuma tanto nuestros módulos del core como el nuevo plugin creado.
</script>
 

Revisión actual del 14:00 15 ene 2021

Prerrequisitos

Para esta práctica solo será necesario que nos aseguremos de tener instalada una versión igual o mayor a Python 3.8.

Para verificar la versión de Python que tenemos instalada podemos ejecutar:

python -V

Preparación para práctica sobre creación de frameworks

En esta práctica recrearemos un desarrollo de un framework para el analisis de modelos de variabilidad.

Podréis encontrar el código en github aquí

Presentación en pdf aquí

Clase grabada aquí

Ejercicio

Iteración 1


  • Análisis: Identificamos los frozen points de nuestro framework
    • Clases del dominio: El dominio sobre el que desarrollaremos nuestro framework es el del analisis de modelos de características. Para ello, comenzaremos por conocer los elementos básicos de los mismos según lo visto en teoría.
    • Gestión de plugins: También analizaremos que partes del framework permitirán extensivilidad basandonos en plugins.
  • Diseño: En este apartado crearemos una clase Feature y una clase Relación para definir los conceptos con los que trabajará el framework

fichero: core/frozen_points_domain.py

from typing import Sequence
#Este modulo añade soporte a clases abstractas
from abc import ABC, abstractmethod

#Esto es la definición de la clase Relación
class Relation:

    #Esto es un constructor en python
    #Cuando colocamos el nombre de la clase entre comillas es la forma de usar tipado fuerte en python. 
    #De cara a crear un framework de caja blanca, necesitamos orientación a objetos y tipado de objectos
    #Esto está disponible en python desde la versión 3.8 en adelante
    def __init__(self, parent: 'Feature', children: Sequence['Feature'], card_min: int, card_max: int):
        self.parent = parent
        self.children = children
        self.card_min = card_min
        self.card_max = card_max

    #Este método añade un elemento a la colección de hijos de una relación
    def add_child(self, feature: 'Feature'):
        self.children.append(feature)

    #Este método verifica la cardinalidad de la relación y el número de hijos para devolver true si es mandatory
    def is_mandatory(self) -> bool:
        return (len(self.children) == 1 and self.card_max == 1 and self.card_min == 1)

    #Este método verifica la cardinalidad de la relación y el número de hijos para devolver true si es optional
    def is_optional(self) -> bool:
        return (len(self.children) == 1 and self.card_max == 1 and self.card_min == 0)

    #Este método verifica la cardinalidad de la relación y el número de hijos para devolver true si es or
    def is_or(self) -> bool:
        return (len(self.children) > 1 and self.card_max == len(self.children) and self.card_min == 1)

    #Este método verifica la cardinalidad de la relación y el número de hijos para devolver true si es alternative
    def is_alternative(self) -> bool:
        return (len(self.children) > 1 and self.card_max == 1 and self.card_min ==  1)

    #Este método to string en python
    def __str__(self):
        res = self.parent.name + '[' + str(self.card_min) + ',' + str(self.card_max) + ']'
        for _child in self.children:
            res += _child.name + ' '
        return res

#Esto es la definición de la clase Feature
class Feature:

    #Este es el método constructor. Aqui definimos que queremos que cada característica tenga un nombre y un conjunto de relaciones. 
    def __init__(self, name: str, relations: Sequence['Relation']):
        self.name = name
        self.relations = relations

    #Este es un metodo auxiliar para añadir una relación a una característica
    def add_relation(self, relation: 'Relation'):
        self.relations.append(relation)

    #Este método nos devuelve el conjunto de relaciones de una característica
    def get_relations(self):
        return self.relations

    #Este método to string en python
    def __str__(self):
        return self.name

#Esta clase representa a un modelo de características
class FeatureModel():

    #En el constructor solo encontramos una feature raiz
    def __init__(self, root: Feature):
        self.root = root

    #Este método devuelve el conjunto de relaciones existentes en el modelo
    def get_relations(self, feature=None):
        relations = []
        if not feature:
            feature = self.root
        for relation in feature.relations:
            relations.append(relation)
            for _feature in relation.children:
                relations.extend(self.get_relations(_feature))
        return relations

    #Este método devuelve el conjunto de features de un modelo
    def get_features(self):
        features = []
        features.append(self.root)
        for relation in self.get_relations():
            features.extend(relation.children)
        return features

    #Este método devuelve la feature identificandola por el nombre (TODO: Implementar con __equals__)
    def get_feature_by_name(self, str) -> Feature:
        features = self.get_features
        for feat in features:
            if feat.name == str:
                return feat
        raise ElementNotFoundException

    #Este método to string en python
    def __str__(self) -> str:
        res = 'root: ' + self.root.name + '\r\n'
        for i, relation in enumerate(self.get_relations()):
            res += f'relation {i}: {relation}\r\n'
        return(res)

#This is an abstract Operation. To define abstract methos we rely on ABC module of the core python distribution
class Operation(ABC):

    #This abstract method, executes an operation given a feature model
    @abstractmethod
    def execute(self, model: FeatureModel) -> 'Operation':
        pass

También implementamos el sistema dentro del core para la carga de plugins fichero: core/frozen_points_plugins.py

from types import FunctionType, ModuleType
from typing import List

# Este modulo nos permite obligar ciertas listas con tipos
from collections import UserList
# Estos modulos nos permiten explorar las clases existentes en distintos plugins 'a la java reflexion' 
from importlib import import_module
from pkgutil import iter_modules
import inspect

# Importamos las clases del core que vamos a necesitar (blackbox)
from core.frozen_points_domain import Operation
from core.frozen_points_domain import FeatureModel

# Definimos las posibles rutas a los plugins
PLUGIN_PATHS = [
    'plugin',
]

# Definimos una coleccion para almacenar los objetos de tipo operacion. 
# Es interesante que las colecciones tipadas funcionan como clases y pueden tener métodos
class Operations(UserList):  # pylint: disable=too-many-ancestors
    data: List[Operation]

    def search_by_name(self, name: str) -> Operation:
        candidates = filter(lambda op: op.__name__ == name, self.data)
        try:
            operation = next(candidates, None)
        except StopIteration:
            raise OperationNotFound
        return operation

# Definimos una clase que represente a un plugin dentro del framwork. 
# Notese que también contiene los modulos y las operaciones que haya en la instalación de python
class Plugin:
    def __init__(self, module: ModuleType) -> None:
        self.module: ModuleType = module
        self.operations: Operations = Operations()

    @property
    def name(self):
        return self.module.__name__.split('.')[-1]
    
    # Esta clase añade las operaciones de un modulo
    def append_operation(self, operation: Operation) -> None:
        self.operations.append(operation)

    def use_operation(self, name: str, src: FeatureModel) -> Operation:
        operation = self.operations.search_by_name(name)
        return operation().execute(model=src)

# Definimos una lista de plugins y algunos métodos importantes
class Plugins(UserList):  # pylint: disable=too-many-ancestors
    data: List[Plugin]

    # buscamos los plugins que se llamen como queremos
    def get_plugin_by_name(self, name: str):
        for plugin in self.data:
            if plugin.name == name:
                return plugin
        raise PluginNotFound

    # devolvemos la lista de plugins            
    def get_plugin_names(self) -> List[str]:
        return [plugin.name for plugin in self.data]

    #devolvemos las operacione sde un plugin
    def get_operations_by_plugin_name(self, plugin_name: str) -> Operations:
        try:
            plugin = self.get_plugin_by_name(plugin_name)
            return plugin.operations
        except PluginNotFound:
            return Operations()

#Esta es una clase especial que nos va a permitir enumerar y ejecutar las operaciones que tengamos dentro de los plugins
class DiscoverMetamodels:
    
    #Cuando creamos la clase, esta inicializa la lista de modulos existentes y llama al discover para buscar plugins y modulos 
    def __init__(self):
        self.module_paths: List[ModuleType] = list()
        for path in PLUGIN_PATHS:
            try:
                module: ModuleType = import_module(path)
                self.module_paths.append(module)
            except ModuleNotFoundError:
                print('ModuleNotFoundError %s', path)
        self.plugins: Plugins = self.discover()

    #Este metodo busca las clases existentes en los módulos encontrados
    def search_classes(self, module):
        classes = []
        for _, file_name, ispkg in iter_modules( module.__path__, module.__name__ + '.' ):
            if ispkg:
                classes += self.search_classes(import_module(file_name))
            else:
                _file = import_module(file_name)
                classes += inspect.getmembers(_file, inspect.isclass)
        return classes

    # Este método se encarga de buscar los plugins modulos y clases existentes en el path indicado como variable global
    def discover(self) -> dict:
        plugins = Plugins()
        for pkg in self.module_paths:
            for _, plugin_name, ispkg in iter_modules(pkg.__path__, pkg.__name__ + '.'):
                if not ispkg:
                    continue
                module = import_module(plugin_name)
                plugin = Plugin(module=module)
                classes = self.search_classes(module)
                for _, _class in classes:
                    if not _class.__module__.startswith(module.__package__):
                        continue  # Exclude modules not in current package
                    #!! Fijaos como añadimos a la colección de operaciones cuando la clase operaciones hereda de la clase abstracta !!
                    inherit = _class.mro()
                    if Operation in inherit:
                        plugin.append_operation(_class)   
                plugins.append(plugin)
        return plugins

Finalmente implementaremos un plugin fichero: plugin/count_leafs/count_leafs_op.py

from core.frozen_points_domain import Operation
from core.frozen_points_domain import FeatureModel

class CountLeafs(Operation):

    def execute(self, model: FeatureModel) -> 'Operation':
        result = 0
        for feat in model.get_features():
            if len(feat.get_relations())==0:
                result= result +1
        return result


  • Instanciación: Finalmente implementaremos una aplicación que consuma tanto nuestros módulos del core como el plugin creado.

fichero:./main.py

from core.frozen_points_domain import *
from core.frozen_points_plugins import DiscoverMetamodels

# Creamos el manager y lo inicializamos
dm = DiscoverMetamodels()
#Buscamos los plugins disponibles
available_plugins = dm.discover()

#Creamos un modelo de manera programatica
feature_b = Feature('B', [])
relation = Relation(parent=None, children=[feature_b], card_min=0, card_max=1)	
feature_a = Feature('A', [relation])	
relation.parent = feature_a
fm = FeatureModel(feature_a)

#Imprimimos ese modelo
print(fm)

#Imprimimos los plugins disponibles
print(available_plugins.get_plugin_names())

#Buscamos el plugin que acabamos de crear
plugin=available_plugins.get_plugin_by_name('count_leafs')

#Imprimimos las operaciones de los plugins
print(plugin.operations)

#Usamos la operacion CountLeafs
result=plugin.use_operation('CountLeafs',fm)

#Imprimimos el resultado
print("El modelo tiene " + result + "Features hojas")

--IMPORTANTE-- Añadir los ficheros __init__.py para que las carpetas core y core_leafs sean modulos Python

Iteración 2


Se ha introducido un nuevo requisito para un nuevo producto en el que se nos pide una operación que cuente el número de productos del modelo. TIEMPO 20 minutos

  • Análisis: Identificamos los frozen points de nuestro framework al solicitarnos la implementación de una operación que nos diga cuantas características tiene un modelo
    • Clases del dominio: Añadiremos las clases necesarias para implementar distintos tipos de serializadores de ficheros.
  • Diseño: En este apartado crearemos un nuevo plugin que implemente esa operación
  • Instanciación: Finalmente implementaremos una aplicación que consuma tanto nuestros módulos del core como el nuevo plugin creado.