J'aimerais trouver une solution simple à mon problème mais je n'y arrive pas.
Cela parait pourtant simple ...
J'ai un équipement qui est capable de communiquer de plusieurs façon (Par exemple LoRaWAN & Bluetooth).
Par exemple supposons que cette équipement possède un GPS.
J'aimerais pourvoir changer rapidement de canal de transmission pour remplir mon attribut "GPS".
J'aimerais alors pouvoir récupérer la valeur de la position en faisant simplement un :
print(Equipement.GPS.getGPSPosition())
J'aimerais que mon code soit le plus modulaire possible afin que je puisse facilement rajouter par la suite par exemple un capteur sans embarquer le "transportChannel" dans la classe GPS, Capteur etc etc ...
Avec un code d'exemple c'est plus parlant :
import abc
class ITransportChannel(abc.ABC):
@abc.abstractmethod
def getGPSPosition(self) -> (float,float):
return 0.0,0.0
class LoRaWAN(ITransportChannel):
def getGPSPosition(self):
"""
Get GPS position throw LoRaWAN Channel
"""
return 1.0,2.0
class Bluetooth(ITransportChannel):
def getGPSPosition(self):
"""
Get GPS position throw LoRaWAN Channel
"""
return 3.0,5.0
class GPS:
def __init__(self):
self.__latitude: float = 0.0
self.__longitude: float = 0.0
def getGPSPosition(self):
return self.__latitude, self.__longitude
class Transport:
def __init__(self):
self.__channel : ITransportChannel | None = None
def setChannel(self, channel:str):
if channel == "LoRaWAN":
self.__channel = LoRaWAN()
elif channel == "Bluetooth":
self.__channel = Bluetooth()
else:
raise "Only LoRaWAN and Bluetooth are supported"
def getChannel(self):
return self.__channel
class Equipement:
def __init__(self):
self.GPS:GPS|None = GPS()
self.Transport:Transport|None = Transport()
Node = Equipement()
print(Node.GPS.getGPSPosition())
Node.Transport.setChannel("LoRaWAN")
print(Node.Transport.getChannel().getGPSPosition())
Node.Transport.setChannel("Bluetooth")
print(Node.Transport.getChannel().getGPSPosition())
Merci pour votre aide !
Kasimashi
- Edité par Kasimashi 25 septembre 2024 à 22:17:01
J'aimerais trouver une solution simple à mon problème mais je n'y arrive pas.
Il n'y a pas de solution simple sinon commencer par poser le problème en tant que POO et connaître les différents patterns pour voir ce qui seraient applicables ici (comme par exemple le factory pattern)... et un problème de conception n'est pas encore un sujet de programmation python.
Pour moi, si je devais respecter le principe SOLID, je ferai quelques modifications (ci-dessous).
from abc import ABC, abstractmethod
from typing import Tuple
class ITransportChannel(ABC):
@abstractmethod
def getGPSPosition(self) -> Tuple[float, float]:
pass
class LoRaWAN(ITransportChannel):
def getGPSPosition(self) -> Tuple[float, float]:
return 1.0,2.0
class Bluetooth(ITransportChannel):
def getGPSPosition(self) -> Tuple[float, float]:
return 3.0,5.0
class Transport(ABC):
def getChannel(self) -> ITransportChannel:
return self._channel
class TransportLoRaWan(Transport):
def __init__(self):
self._channel: ITransportChannel = LoRaWAN()
class TransportBluetooth(Transport):
def __init__(self):
self._channel: ITransportChannel = Bluetooth()
Qu'en pensez-vous ?
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard) La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Donc si je résume : mon problème est donc de trouver un design pattern pour répondre à mon besoin et de mettre cela plutôt sous diagramme UML pour trouver une solution.
J'avoue ne pas être expert UML pour le coup. Bien que j'arrive à comprendre malgré tout les diagrammes mais quand il faut les faires c'est une autre paire de manche !
PS : Pour le choix du transport, j'ai opté pour le design pattern strategy. Mais mon problème est donc de compléter mes attributs (GPS etc ...) en passant par ce pattern.
A savoir que suivant le canal le code appelé peut être assez lourd derrière. Et j'aimerais éviter de faire de la redondance dans mon code.
En utilisant mon code vous pourrez utiliser ce pattern, vous pouvez externaliser la logique de choix du canal de transport et permettre à l'utilisateur de choisir dynamiquement ou changer de stratégie à tout moment.
grosso modo ça donne cela,
from abc import ABC, abstractmethod
from typing import Tuple
class ITransportChannel(ABC):
@abstractmethod
def getGPSPosition(self) -> Tuple[float, float]:
pass
class LoRaWAN(ITransportChannel):
def getGPSPosition(self) -> Tuple[float, float]:
return 1.0, 2.0
class Bluetooth(ITransportChannel):
def getGPSPosition(self) -> Tuple[float, float]:
return 3.0, 5.0
class Transport:
def __init__(self, channel: ITransportChannel):
self._channel = channel
def set_channel(self, channel: ITransportChannel):
self._channel = channel
def getGPSPosition(self) -> Tuple[float, float]:
return self._channel.getGPSPosition()
transport = Transport(LoRaWAN())
print(transport.getGPSPosition())
transport.set_channel(Bluetooth())
print(transport.getGPSPosition())
On peut toujours ergoter sur le ou les patterns qui pourraient être intéressants
Je ne précise aucun pattern, SOLID n'est pas un pattern... mais des principes de conception, c'est du design de code
- Edité par fred1599 26 septembre 2024 à 13:57:50
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard) La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
J'ai repris votre code en intégrant maintenant mon équipement :
Ce qui m'intéresserait est maintenant est de remplir l'objet GPS en pouvant changer dynamiquement le canal de transport et c'est là justement que je bug
Transport devient une surcouche de la classe Equipement (Equipment c'est mieux - terme anglais).
Du coup je ne vois plus son intérêt... ainsi que la classe GPS où je ne vois pas ce qu'elle apporte, mais n'ayant pas les tenants et les aboutissants...
Est-ce qu'on pourrait pas faire plus simple, par ex.
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard) La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Le problème c'est qu'en réalité dans mon code mon équipement possède un très grand nombre d'attributs et donc de méthodes (D'ailleurs je le construit avec le design pattern builder).
Donc cela voudrait dire mettre toutes les méthodes dans la classe Equipment.
J'aimerais faire un code le plus propre possible en ce sens en compartimentant mon code par fonctionnalités.
Je cherche actuellement ... J'essai actuellement de tourner le code de façon différentes.
Peut être en créant une classe abstraire "EquipementCapabilities" qui décrit toutes les méthodes qui doivent être implémentés ?
En soit en toute rigueur si on veut rajouter des méthodes et passer par des interfaces c'est plus rigoureux mais le code devient vite indigeste ...
D'où l'idée de rajouter des attributs qui sont des objets "complexes"
Donc l'ajout d'une classe GPS est je pense intéressante.
Par exemple avec l'ajout de l'information du processeur :
Je ne connais pas suffisamment ta problématique pour en dire plus, mais faut garder ça en tête dans les limites des possibilités :
Une classe -> une responsabilité
Une méthode -> une tâche liée à l'objet
Si paramètre dans une méthode -> utiliser l'interface et non la classe
Ne jamais avoir à modifier une méthode pour un autre cas, on peut en ajouter, mais pas là modifier
... bref lis wikipedia et le concept SOLID.
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard) La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
En attendant ton code peut être beaucoup plus simple, j'ai pris le temps d'analyser sans être trop influencé par tes demandes.
from typing import Tuple
class ITransportChannel:
def __init__(self, position=None, processor=None):
self.__position = position
self.__processor = processor
@property
def position(self):
return self.__position
@property
def processor(self):
return self.__processor
class LoRaWAN(ITransportChannel):
pass
class Bluetooth(ITransportChannel):
pass
class Equipement:
def getGPSPosition(self, channel: ITransportChannel) -> Tuple[float, float]:
return channel.position
def getProcessor(self, channel: ITransportChannel) -> str:
return channel.processor
equipment = Equipement()
lorawan = LoRaWAN(position=(1.0, 2.0), processor="MyProcessor")
bluetooth = Bluetooth(position=(3.0, 5.0), processor="MyProcessor")
print(equipment.getGPSPosition(lorawan))
print(equipment.getGPSPosition(bluetooth))
print(equipment.getProcessor(bluetooth))
À l'heure actuelle pas besoin d'interface...
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard) La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Je viens de pondre un autre code de test voir en utilisant le pattern Observer :
J'étudie ta proposition : Merci beaucoup pour ton aide !
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import List,Tuple
class ITransportManager(ABC):
"""
The Subject interface declares a set of methods for managing subscribers.
"""
@abstractmethod
def attach(self, observer: Observer) -> None:
"""
Attach an observer to the subject.
"""
pass
@abstractmethod
def detach(self, observer: Observer) -> None:
"""
Detach an observer from the subject.
"""
pass
@abstractmethod
def notify(self) -> None:
"""
Notify all observers about an event.
"""
pass
class IGPS(ABC):
@abstractmethod
def getGPSPosition(self) -> Tuple[float, float]:
pass
class IProcessor(ABC):
@abstractmethod
def getProcessor(self) -> str:
pass
class EquipmentCapabilities(IGPS, IProcessor):
pass
class ITransportChannel(IGPS, IProcessor):
pass
class TransportManager(ITransportManager):
"""
List of subscribers. In real life, the list of subscribers can be stored
more comprehensively (categorized by event type, etc.).
"""
def __init__(self):
self.__transport = None
self.__observers: List[Observer] = []
def attach(self, observer: Observer) -> None:
print(f"Transport: Attached an observer. {observer}")
self.__observers.append(observer)
def detach(self, observer: Observer) -> None:
self.__observers.remove(observer)
"""
The subscription management methods.
"""
def notify(self) -> None:
"""
Trigger an update in each subscriber.
"""
print("Subject: Notifying observers...")
for observer in self.__observers:
observer.updateTransport(self.__transport)
def setTransport(self, transport: ITransportChannel) -> None:
"""
Usually, the subscription logic is only a fraction of what a Subject can
really do. Subjects commonly hold some important business logic, that
triggers a notification method whenever something important is about to
happen (or after it).
"""
self.__transport = transport
self.notify()
class LoRaWAN(ITransportChannel):
def getGPSPosition(self) -> Tuple[float, float]:
return 1.0, 2.0
def getProcessor(self) -> str:
return "MyProcessor from LoRaWAN"
class Bluetooth(ITransportChannel):
def getGPSPosition(self) -> Tuple[float, float]:
return 5.0, 3.0
def getProcessor(self) -> str:
return "MyProcessor from Bluetooth"
class Observer(ABC):
"""
The Observer interface declares the update method, used by subjects.
"""
@abstractmethod
def updateTransport(self, channel: ITransportChannel) -> None:
"""
Receive update from subject.
"""
pass
class GPS(Observer, IGPS):
def __init__(self):
self.__channel: ITransportChannel | None = None
self.__lat:float = 0.0
self.__long: float = 0.0
def updateTransport(self, channel: ITransportChannel) -> None:
print(f"{self} : Channel is now {channel}")
self.__channel = channel
def getGPSPosition(self):
self.__lat, self.__long = self.__channel.getGPSPosition()
return self.__lat, self.__long
class Processor(Observer, IProcessor):
def __init__(self):
self.__channel:ITransportChannel | None = None
self.__processor:str = ""
def updateTransport(self, channel: ITransportChannel) -> None:
print(f"{self} : Channel is now {channel}")
self.__channel = channel
def getProcessor(self) -> str:
self.__processor = self.__channel.getProcessor()
return self.__processor
if __name__ == "__main__":
# The client code.
transport = TransportManager()
gps = GPS()
processor = Processor()
transport.attach(gps)
transport.attach(processor)
transport.setTransport(LoRaWAN())
print(gps.getGPSPosition())
print(processor.getProcessor())
transport.setTransport(Bluetooth())
print(gps.getGPSPosition())
print(processor.getProcessor())
transport.detach(gps)
Sortie :
Transport: Attached an observer. <__main__.GPS object at 0x000001F7AEE29DE0>
Transport: Attached an observer. <__main__.Processor object at 0x000001F7AEE29960>
Subject: Notifying observers...
<__main__.GPS object at 0x000001F7AEE29DE0> : Channel is now <__main__.LoRaWAN object at 0x000001F7AEE29540>
<__main__.Processor object at 0x000001F7AEE29960> : Channel is now <__main__.LoRaWAN object at 0x000001F7AEE29540>
(1.0, 2.0)
MyProcessor from LoRaWAN
Subject: Notifying observers...
<__main__.GPS object at 0x000001F7AEE29DE0> : Channel is now <__main__.Bluetooth object at 0x000001F7AEE29450>
<__main__.Processor object at 0x000001F7AEE29960> : Channel is now <__main__.Bluetooth object at 0x000001F7AEE29450>
(5.0, 3.0)
MyProcessor from Bluetooth
As-tu la possibilité de définir la responsabilité de chacune de tes classes (non abstraites) ?
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard) La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Donc si je résume : mon problème est donc de trouver un design pattern pour répondre à mon besoin et de mettre cela plutôt sous diagramme UML pour trouver une solution.
J'avoue ne pas être expert UML pour le coup.
Votre problème est un problème de conception où classes et relations à construire entre elles sont à définir... et probablement que connaître des design patterns permettrait de savoir à quoi elles pourraient ressembler.
UML c'est sympa pour les dessins mais si vous regardez un peu comment sont définis les patterns OO, ils s'en passent très bien.
As-tu la possibilité de définir la responsabilité de chacune de tes classes (non abstraites) ?
J'avoue que je n'ai pas trop compris la question
mps a écrit:
Kasimashi a écrit:
Donc si je résume : mon problème est donc de trouver un design pattern pour répondre à mon besoin et de mettre cela plutôt sous diagramme UML pour trouver une solution.
J'avoue ne pas être expert UML pour le coup.
Votre problème est un problème de conception où classes et relations à construire entre elles sont à définir... et probablement que connaître des design patterns permettrait de savoir à quoi elles pourraient ressembler.
UML c'est sympa pour les dessins mais si vous regardez un peu comment sont définis les patterns OO, ils s'en passent très bien.
Je n'ai jamais vraiment fait d'architecture logiciel. Donc ça ne va pas être simple. Honnêtement : Je ne voyais pas à l'époque l'utilité de la chose maintenant je comprend mieux ... ;)
A part si vous souhaitez m'aider pour la mise en place de mon architecture je veux bien ahah sinon je pense que je peux passer le sujet en résolu.
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard) La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Utiliser plusieurs canaux pour affecter un attrib
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)
Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)