Add notes about Js, diffsitter and solid
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
:PROPERTIES:
|
:PROPERTIES:
|
||||||
:ID: ca50d517-3e8a-4d03-ba38-7ff411e87408
|
:ID: ca50d517-3e8a-4d03-ba38-7ff411e87408
|
||||||
:mtime: 20220602223012
|
:mtime: 20220910133708
|
||||||
:ctime: 20211230101331
|
:ctime: 20211230101331
|
||||||
:END:
|
:END:
|
||||||
#+title: Software
|
#+title: Software
|
||||||
@@ -25,17 +25,21 @@ TODO
|
|||||||
** Interprétés
|
** Interprétés
|
||||||
*** [[id:a877b82e-4925-41de-8903-8109dd98e773][Bash]]
|
*** [[id:a877b82e-4925-41de-8903-8109dd98e773][Bash]]
|
||||||
*** [[id:33ef1e68-70ad-43c8-850d-4b8ed2c5ea16][Elisp]]
|
*** [[id:33ef1e68-70ad-43c8-850d-4b8ed2c5ea16][Elisp]]
|
||||||
|
*** [[id:d4197090-4ee5-4fa3-ba2d-a70ce9953923][Javascript]]
|
||||||
*** [[id:4fabfe6a-b104-464f-8a87-dfd7d761dbcc][Python]]
|
*** [[id:4fabfe6a-b104-464f-8a87-dfd7d761dbcc][Python]]
|
||||||
|
|
||||||
* Composition de docs
|
* Composition de docs
|
||||||
** [[id:3250943b-32f0-4bdc-b0d2-5fbcf1724f36][Latex]]
|
** [[id:3250943b-32f0-4bdc-b0d2-5fbcf1724f36][Latex]]
|
||||||
|
|
||||||
* Méthodes
|
* Méthodes/Principes
|
||||||
|
** [[id:b827c6e7-fe86-4301-a72c-dfaee85e142f][SOLID]]
|
||||||
** [[id:6da0b985-e6f4-4454-bb6a-e941b722365b][Test driven development]]
|
** [[id:6da0b985-e6f4-4454-bb6a-e941b722365b][Test driven development]]
|
||||||
|
|
||||||
* Gestionnaire de versions
|
* Gestionnaire de configuration
|
||||||
** [[id:e93719b3-088d-4fe7-9ef8-fc9a4fd84827][Git]]
|
** [[id:e93719b3-088d-4fe7-9ef8-fc9a4fd84827][Git]]
|
||||||
|
|
||||||
* Outils
|
* Outils
|
||||||
|
** Diff
|
||||||
|
*** [[id:67317dba-3771-405f-ae58-0ef13062975d][diffsitter]]
|
||||||
** Performances
|
** Performances
|
||||||
*** [[https://www.intel.com/content/www/us/en/developer/tools/oneapi/vtune-profiler.html#gs.26xa9e][Intel® VTune™ Profiler]]
|
*** [[https://www.intel.com/content/www/us/en/developer/tools/oneapi/vtune-profiler.html#gs.26xa9e][Intel® VTune™ Profiler]]
|
||||||
|
12
20220825074527-javascript.org
Normal file
12
20220825074527-javascript.org
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: d4197090-4ee5-4fa3-ba2d-a70ce9953923
|
||||||
|
:mtime: 20220825074959
|
||||||
|
:ctime: 20220825074527
|
||||||
|
:END:
|
||||||
|
#+title: Javascript
|
||||||
|
|
||||||
|
* Introduction
|
||||||
|
Language interprété très largement utilisé pour les clients web. Il est aussi utilisé pour des applications backend ou
|
||||||
|
standalone via /v8/ ou [[https://deno.land/][Deno]].
|
||||||
|
|
||||||
|
* Références
|
14
20220825080435-diffsitter.org
Normal file
14
20220825080435-diffsitter.org
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: 67317dba-3771-405f-ae58-0ef13062975d
|
||||||
|
:mtime: 20220825080750
|
||||||
|
:ctime: 20220825080435
|
||||||
|
:END:
|
||||||
|
#+title: diffsitter
|
||||||
|
#+filetags: :TO_TRY:
|
||||||
|
|
||||||
|
* Introduction
|
||||||
|
Outil de diff basé sur l'analyse d'arbres AST plutôt que de chaines de caractères pour identifier les différences entre
|
||||||
|
deux fichiers contenant du code.
|
||||||
|
|
||||||
|
* Références
|
||||||
|
* [[https://github.com/afnanenayet/diffsitter][GitHub - afnanenayet/diffsitter: A tree-sitter based AST difftool to get meaningful semantic diffs]]
|
328
20220908193040-solid.org
Normal file
328
20220908193040-solid.org
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
:PROPERTIES:
|
||||||
|
:ID: b827c6e7-fe86-4301-a72c-dfaee85e142f
|
||||||
|
:mtime: 20220910133550
|
||||||
|
:ctime: 20220908193040
|
||||||
|
:END:
|
||||||
|
#+title: SOLID
|
||||||
|
|
||||||
|
* Introduction
|
||||||
|
* Cinq principes de design de classes (P.O.O),
|
||||||
|
* Principes décrits par Robert J. Martin (a.k.a Uncle Bob) en 2000,
|
||||||
|
* Acronyme créé par Michael Feathers.
|
||||||
|
|
||||||
|
* Principes
|
||||||
|
** Responsabilité unique (Single responsability principle)
|
||||||
|
/A class should have one and only one reason to change, meaning that a class should have only one job./
|
||||||
|
|
||||||
|
Exemple de classe réalisant différentes tâches:
|
||||||
|
* Calcul du total de la facture (méthode ~calculate_total~),
|
||||||
|
* Impression de la facture (méthode ~print_invoice~),
|
||||||
|
* Sauvegarde de la facture (méthode ~save_to_file~).
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :results output
|
||||||
|
class Invoice:
|
||||||
|
|
||||||
|
def __init__(self, book, quantity, discount_rate, tax_rate, total):
|
||||||
|
self.book = book
|
||||||
|
self.quantity = quantity
|
||||||
|
self.discount_rate = discount_rate
|
||||||
|
self.tax_rate = tax_rate
|
||||||
|
self.total = self.calculate_total()
|
||||||
|
|
||||||
|
def calculate_total(self):
|
||||||
|
price = (self.book.price - self.book.price * self.discount_rate) * self.quantity
|
||||||
|
price_with_taxe = price * (1 + self.tax_rate)
|
||||||
|
return price_with_taxe
|
||||||
|
|
||||||
|
def print_invoice(self):
|
||||||
|
print(f'{self.quantity}x {self.name} {self.price} $')
|
||||||
|
print(f'Discount Rate: {self.discount_rate}')
|
||||||
|
print(f'Tax Rate: {self.tax_rate}')
|
||||||
|
print(f'Total: {self.total}')
|
||||||
|
|
||||||
|
def save_to_file(self):
|
||||||
|
# Creates a file with given name and writes the invoice
|
||||||
|
...
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Les erreurs :
|
||||||
|
* La logique d'impression d'une facture (méthode ~print_invoice~) devrait étre isolée : la seule raison pour laquelle
|
||||||
|
la classe ~Invoice~ devrait étre mise à jour est le changement de logique de calul du total (méthode
|
||||||
|
~calculate_total~).
|
||||||
|
* La logique de sauvegarde d'une facture (méthode ~save_to_file~) devrait elle aussi étre isolée : la classe ~Invoice~
|
||||||
|
ne devrait pas étre mise à jour suite au changement de méthode de sauvegarde (passage d'un fichier à une BDD par ex).
|
||||||
|
|
||||||
|
Une implémetation respectant le principe de *responsabilité unique* :
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :results output
|
||||||
|
class Invoice:
|
||||||
|
|
||||||
|
def __init__(self, book, quantity, discount_rate, tax_rate, total):
|
||||||
|
self.book = book
|
||||||
|
self.quantity = quantity
|
||||||
|
self.discount_rate = discount_rate
|
||||||
|
self.tax_rate = tax_rate
|
||||||
|
self.total = self.calculate_total()
|
||||||
|
|
||||||
|
def calculate_total(self):
|
||||||
|
price = (self.book.price - self.book.price * self.discount_rate) * self.quantity
|
||||||
|
price_with_taxe = price * (1 + self.tax_rate)
|
||||||
|
return price_with_taxe
|
||||||
|
|
||||||
|
|
||||||
|
class InvoicePrinter:
|
||||||
|
|
||||||
|
def __init__(self, invoice):
|
||||||
|
self._invoice = invoice
|
||||||
|
|
||||||
|
def print(self):
|
||||||
|
print(f'{self._invoice.quantity}x {self._invoice.name} {self._invoice.price} $')
|
||||||
|
print(f'Discount Rate: {self._invoice.discount_rate}')
|
||||||
|
print(f'Tax Rate: {self._invoice.tax_rate}')
|
||||||
|
print(f'Total: {self._invoice.total}')
|
||||||
|
|
||||||
|
|
||||||
|
class InvoicePersistance:
|
||||||
|
|
||||||
|
def __init__(self, invoice):
|
||||||
|
self._invoice = invoice
|
||||||
|
|
||||||
|
def save_to_file(self):
|
||||||
|
# Creates a file with given name and writes the invoice
|
||||||
|
...
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
** Ouvert-fermé (Open-close principle)
|
||||||
|
/Objects or entities should be open for extension but closed for modification./
|
||||||
|
|
||||||
|
Une classe doit étre *ouverte pour l'extension* et *fermée pour les modifications* :
|
||||||
|
* Il doit étre possible d'ajouter une fonctionnalité à une classe sans modifier le code existant,
|
||||||
|
* Réalisé à l'aide d'interfaces et classes virtuelles,
|
||||||
|
|
||||||
|
Exemple de classe ne respesctant pas le principe :
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :results output
|
||||||
|
class InvoicePersistance:
|
||||||
|
|
||||||
|
def __init__(self, invoice):
|
||||||
|
self._invoice = invoice
|
||||||
|
|
||||||
|
def save_to_file(self):
|
||||||
|
# Creates a file with given name and writes the invoice
|
||||||
|
...
|
||||||
|
|
||||||
|
def save_to_database(self):
|
||||||
|
# Save the invoice to database
|
||||||
|
...
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
Il ne devrait pas étre nécessaire de modifier la classe ~InvoicePersistance~ lorsqu'une nouvelle manière de sauvegarder
|
||||||
|
une ~Invoice~ est requise.
|
||||||
|
|
||||||
|
Une implémetation respectant le principe *ouvert/fermé* :
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :results output
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
class InvoicePersistance():
|
||||||
|
|
||||||
|
def __init__(self, invoice):
|
||||||
|
self._invoice = invoice
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def save(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class FileInvoicePersistance(InvoicePersistance):
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
# Creates a file with given name and writes the invoice
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseInvoicePersistance(InvoicePersistance):
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
# Save the invoice to database
|
||||||
|
...
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
** Principe de substitution de Liskov
|
||||||
|
/Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T./
|
||||||
|
|
||||||
|
Les classes filles doivent pouvoir être substituées par leur classe mère :
|
||||||
|
* Pour une classe B, fille de A, il doit être possible de passer une instance de B à toute méthode s'attendant à avoir une instance de A, sans disfonctionnement.
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :results output
|
||||||
|
class Rectangle:
|
||||||
|
|
||||||
|
def __init__(self, width, height):
|
||||||
|
self._width = width
|
||||||
|
self._height = height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def width(self):
|
||||||
|
return self._width
|
||||||
|
|
||||||
|
@width.setter
|
||||||
|
def width(self, width):
|
||||||
|
self._width = width
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height(self):
|
||||||
|
return self._height
|
||||||
|
|
||||||
|
@height.setter
|
||||||
|
def height(self, height):
|
||||||
|
self._height = height
|
||||||
|
|
||||||
|
def get_area(self):
|
||||||
|
return self._width * self._height
|
||||||
|
|
||||||
|
|
||||||
|
class Square(Rectangle):
|
||||||
|
|
||||||
|
def __init__(self, size):
|
||||||
|
super().__init__(size, size)
|
||||||
|
|
||||||
|
@Rectangle.width.setter
|
||||||
|
def width(self, size):
|
||||||
|
self._width = size
|
||||||
|
self._height = size
|
||||||
|
|
||||||
|
@Rectangle.height.setter
|
||||||
|
def height(self, size):
|
||||||
|
self._width = size
|
||||||
|
self._height = size
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_area(rectangle):
|
||||||
|
width = rectangle.width
|
||||||
|
rectangle.height = 10
|
||||||
|
print(f'Expected area of {10 * width}, got {rectangle.get_area()}')
|
||||||
|
|
||||||
|
rectangle = Rectangle(2, 3)
|
||||||
|
test_get_area(rectangle)
|
||||||
|
|
||||||
|
square = Square(2)
|
||||||
|
square.width = 5
|
||||||
|
test_get_area(square)
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
#+RESULTS:
|
||||||
|
: Expected area of 20, got 20
|
||||||
|
: Expected area of 50, got 100
|
||||||
|
|
||||||
|
La classe ~Square~ ne respecte pas le principe car le comportement de la méthode ~get_area~ diffère selon l'instance de
|
||||||
|
classe passée en paramètre.
|
||||||
|
|
||||||
|
** Principe de ségrégation d'interface (Interface segregation principle)
|
||||||
|
/A client should never be forced to implement an interface that it doesn’t use, or clients shouldn’t be forced to depend on methods they do not use./
|
||||||
|
|
||||||
|
Il s'agit d'un principe de séparation des interfaces : plusieurs interfaces spécifiques sont meilleures qu'une unique
|
||||||
|
générale. Ainsi, seules les méthodes nécessaires sont implémentées et non l'intégralité de celles définies par
|
||||||
|
l'interface générique.
|
||||||
|
|
||||||
|
Exemple d'implémentation ne respectant pas le principe : l'interface ~ParkingLot~ englobe le stationnement
|
||||||
|
ainsi que le paiement. Quid de son implémentation pour le cas d'un parking gratuit ?
|
||||||
|
L'ideal serait d'isoler la partie paiement dans une interface dédiée.
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :results output
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
class Car:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class ParkingLot:
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def park_car(self):
|
||||||
|
# Decrease empty spot count by 1
|
||||||
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def unpark_car(self):
|
||||||
|
# Increase empty spot count by 1
|
||||||
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_capacity(self):
|
||||||
|
# Return car capacity
|
||||||
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def calculate_fee(self, car):
|
||||||
|
# Return the price based on the number of hours
|
||||||
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def do_payment(self, car):
|
||||||
|
# Do the payment process for the computed fee
|
||||||
|
...
|
||||||
|
|
||||||
|
class FreeParking(ParkingLot):
|
||||||
|
|
||||||
|
def park_car(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def unpark_car(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_capacity(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def calculate_fee(self, car):
|
||||||
|
# Unused method here... the parking is free.
|
||||||
|
...
|
||||||
|
|
||||||
|
def do_payment(self, car):
|
||||||
|
# Unused method here... the parking is free.
|
||||||
|
...
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
** Principe d'inversion de dépendence (Dependency inversion principle)
|
||||||
|
/Entities must depend on abstractions, not on concretions. It states that the high-level module must not depend on the
|
||||||
|
low-level module, but they should depend on abstractions./
|
||||||
|
|
||||||
|
Les classes devraient être baséees sur des interfaces ou des classes abstraites plutôt que des classes concrètes,
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :results output
|
||||||
|
class SqlConnection:
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
class PasswordReminder:
|
||||||
|
|
||||||
|
def __init__(self, connection: SqlConnection) -> None:
|
||||||
|
...
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
La classe ~PasswordReminder~ ne doit pas avoir de couplage fort avec la méthode employée pour obtenir le mot de
|
||||||
|
passe. L'usage d'une interface permettrait d'abstraire la méthode de collecte.
|
||||||
|
|
||||||
|
#+BEGIN_SRC python :results outputs
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
class Connection:
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def connect(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
class SqlConnection(Connection):
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
class PasswordReminder:
|
||||||
|
|
||||||
|
def __init__(self, connection: Connection) -> None:
|
||||||
|
...
|
||||||
|
#+END_SRC
|
||||||
|
|
||||||
|
* Références
|
||||||
|
* [[https://fi.ort.edu.uy/innovaportal/file/2032/1/design_principles.pdf][Uncle bob design principes]]
|
||||||
|
* [[https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/][SOLID principes explained in plain english - freecodecamp.org]]
|
Reference in New Issue
Block a user