Recherche…


Introduction

Un modèle de conception est une solution générale à un problème courant dans le développement de logiciels. Cette rubrique de documentation vise spécifiquement à fournir des exemples de modèles de conception courants en Python.

Modèle de stratégie

Ce modèle de conception est appelé modèle de stratégie. Il est utilisé pour définir une famille d'algorithmes, encapsule chacun d'eux et les rend interchangeables. Le modèle de conception de stratégie permet à un algorithme de varier indépendamment des clients qui l'utilisent.

Par exemple, les animaux peuvent «marcher» de différentes manières. La marche pourrait être considérée comme une stratégie mise en œuvre par différents types d’animaux:

from types import MethodType


class Animal(object):
    
    def __init__(self, *args, **kwargs):
        self.name = kwargs.pop('name', None) or 'Animal'
        if kwargs.get('walk', None):
            self.walk = MethodType(kwargs.pop('walk'), self)

    def walk(self):
        """
        Cause animal instance to walk
        
        Walking funcionallity is a strategy, and is intended to
        be implemented separately by different types of animals.
        """
        message = '{} should implement a walk method'.format(
            self.__class__.__name__)
        raise NotImplementedError(message)


# Here are some different walking algorithms that can be used with Animal
def snake_walk(self):
    print('I am slithering side to side because I am a {}.'.format(self.name))

def four_legged_animal_walk(self):
    print('I am using all four of my legs to walk because I am a(n) {}.'.format(
        self.name))

def two_legged_animal_walk(self):
    print('I am standing up on my two legs to walk because I am a {}.'.format(
        self.name))

L'exécution de cet exemple produirait la sortie suivante:

generic_animal = Animal()
king_cobra = Animal(name='King Cobra', walk=snake_walk)
elephant = Animal(name='Elephant', walk=four_legged_animal_walk)
kangaroo = Animal(name='Kangaroo', walk=two_legged_animal_walk)

kangaroo.walk()
elephant.walk()
king_cobra.walk()
# This one will Raise a NotImplementedError to let the programmer
# know that the walk method is intended to be used as a strategy.
generic_animal.walk()

    # OUTPUT:
    #
    # I am standing up on my two legs to walk because I am a Kangaroo.
    # I am using all four of my legs to walk because I am a(n) Elephant.
    # I am slithering side to side because I am a King Cobra.
    # Traceback (most recent call last):
    #   File "./strategy.py", line 56, in <module>
    #     generic_animal.walk()
    #   File "./strategy.py", line 30, in walk
    #     raise NotImplementedError(message)
    # NotImplementedError: Animal should implement a walk method 

Notez que dans des langages comme C ++ ou Java, ce modèle est implémenté en utilisant une classe abstraite ou une interface pour définir une stratégie. En Python, il est plus logique de définir des fonctions externes pouvant être ajoutées dynamiquement à une classe à l'aide de types.MethodType .

Introduction aux motifs de conception et Singleton Pattern

Les modèles de conception apportent des solutions aux commonly occurring problems de la conception de logiciels. Les modèles de conception ont été introduits pour la première fois par le GoF(Gang of Four) où ils ont décrit les modèles communs comme des problèmes qui se répètent et des solutions à ces problèmes.

Les modèles de conception ont quatre éléments essentiels:

  1. The pattern name est un descripteur que nous pouvons utiliser pour décrire un problème de conception, ses solutions et ses conséquences en un mot ou deux.
  2. The problem décrit quand appliquer le modèle.
  3. The solution décrit les éléments qui composent la conception, leurs relations, leurs responsabilités et leurs collaborations.
  4. The consequences sont les résultats et les compromis de l'application du modèle.

Avantages des modèles de conception:

  1. Ils sont réutilisables dans plusieurs projets.
  2. Le niveau architectural des problèmes peut être résolu
  3. Ils ont fait leurs preuves et ont fait leurs preuves, ce qui est l'expérience des développeurs et des architectes.
  4. Ils ont la fiabilité et la dépendance

Les modèles de conception peuvent être classés en trois catégories:

  1. Motif Créatif
  2. Motif structurel
  3. Motif comportemental

Creational Pattern - Ils sont concernés par la façon dont l'objet peut être créé et ils isolent les détails de la création d'objet.

Structural Pattern - Ils conçoivent la structure des classes et des objets afin qu'ils puissent composer pour obtenir des résultats plus importants.

Behavioral Pattern - Ils concernent l'interaction entre les objets et la responsabilité des objets.

Motif Singleton :

C'est un type de creational pattern qui fournit un mécanisme pour n'avoir qu'un objet et un objet d'un type donné et fournit un point d'accès global.

Par exemple, Singleton peut être utilisé dans des opérations de base de données, où nous voulons que l'objet de base de données conserve la cohérence des données.

la mise en oeuvre

Nous pouvons implémenter Singleton Pattern dans Python en créant une seule instance de classe Singleton et en servant à nouveau le même objet.

class Singleton(object):
    def __new__(cls):
        # hasattr method checks if the class object an instance property or not.
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance

s = Singleton()
print ("Object created", s)

s1 = Singleton()
print ("Object2 created", s1)

Sortie:

('Object created', <__main__.Singleton object at 0x10a7cc310>)
('Object2 created', <__main__.Singleton object at 0x10a7cc310>)

Notez que dans des langages comme C ++ ou Java, ce modèle est implémenté en rendant le constructeur privé et en créant une méthode statique qui effectue l'initialisation de l'objet. De cette façon, un objet est créé sur le premier appel et la classe retourne le même objet par la suite. Mais en Python, nous n'avons aucun moyen de créer des constructeurs privés.

Modèle d'usine

Le motif d'usine est également un Creational pattern . Le terme factory signifie qu'une classe est responsable de la création d'objets d'autres types. Il existe une classe qui agit en tant que fabrique et qui est associée à des objets et à des méthodes. Le client crée un objet en appelant les méthodes avec certains paramètres et fabrique l'objet du type souhaité et le renvoie au client.

from abc import ABCMeta, abstractmethod

class Music():
    __metaclass__ = ABCMeta
    @abstractmethod
    def do_play(self):
        pass

class Mp3(Music):
    def do_play(self):
        print ("Playing .mp3 music!")
    
class Ogg(Music):
    def do_play(self):
        print ("Playing .ogg music!")
    
class MusicFactory(object):
    def play_sound(self, object_type):
        return eval(object_type)().do_play()
    
if __name__ == "__main__":
    mf = MusicFactory()
    music = input("Which music you want to play Mp3 or Ogg")
    mf.play_sound(music)

Sortie:

Which music you want to play Mp3 or Ogg"Ogg"
Playing .ogg music!

MusicFactory est la classe de fabrique ici qui crée un objet de type Mp3 ou Ogg fonction du choix de l'utilisateur.

Procuration

L'objet proxy est souvent utilisé pour garantir un accès sécurisé à un autre objet, quelle logique métier interne nous ne voulons pas polluer avec les exigences de sécurité.

Supposons que nous souhaitons garantir que seul l'utilisateur des autorisations spécifiques puisse accéder aux ressources.

Définition du proxy: (il garantit que seuls les utilisateurs capables de voir les réservations pourront utiliser le service reservation_service)

from datetime import date
from operator import attrgetter

class Proxy:
    def __init__(self, current_user, reservation_service):
        self.current_user = current_user
        self.reservation_service = reservation_service

    def highest_total_price_reservations(self, date_from, date_to, reservations_count):
        if self.current_user.can_see_reservations:
            return self.reservation_service.highest_total_price_reservations(
                date_from,
                date_to,
                reservations_count
              )
        else:
            return []

#Models and ReservationService:

class Reservation:
    def __init__(self, date, total_price):
        self.date = date
        self.total_price = total_price

class ReservationService:
    def highest_total_price_reservations(self, date_from, date_to, reservations_count):
        # normally it would be read from database/external service
        reservations = [
            Reservation(date(2014, 5, 15), 100),
            Reservation(date(2017, 5, 15), 10),
            Reservation(date(2017, 1, 15), 50)
        ]

        filtered_reservations = [r for r in reservations if (date_from <= r.date <= date_to)]

        sorted_reservations = sorted(filtered_reservations, key=attrgetter('total_price'), reverse=True)

        return sorted_reservations[0:reservations_count]


class User:
    def __init__(self, can_see_reservations, name):
        self.can_see_reservations = can_see_reservations
        self.name = name

#Consumer service:

class StatsService:
    def __init__(self, reservation_service):
        self.reservation_service = reservation_service

    def year_top_100_reservations_average_total_price(self, year):
        reservations = self.reservation_service.highest_total_price_reservations(
            date(year, 1, 1),
            date(year, 12, 31),
            1
        )

        if len(reservations) > 0:
            total = sum(r.total_price for r in reservations)

            return total / len(reservations)
        else:
            return 0

#Test:
def test(user, year):
    reservations_service = Proxy(user, ReservationService())
    stats_service = StatsService(reservations_service)
    average_price = stats_service.year_top_100_reservations_average_total_price(year)
    print("{0} will see: {1}".format(user.name, average_price))

test(User(True, "John the Admin"), 2017)
test(User(False, "Guest"),         2017)

AVANTAGES
  • nous évitons tout changement dans ReservationService lorsque les restrictions d'accès sont modifiées.
  • Nous ne date_from pas les données liées à l'entreprise ( date_from , date_to , reservations_count ) avec les concepts non liés au domaine (autorisations utilisateur) en service.
  • Consumer ( StatsService ) est également exempt de logique liée aux autorisations

CAVEATS
  • L'interface proxy est toujours exactement identique à l'objet qu'elle cache, de sorte que l'utilisateur qui consomme le service encapsulé par un proxy n'était même pas au courant de la présence de proxy.


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow