Pattern, Abstract Factory / Factory

Pascal Bourguignon pjb at informatimago.com
Sun Feb 7 08:14:03 UTC 2021


Le 07/02/2021 à 08:58, Marco Antoniotti a écrit :
> OK.
> 
> procedure foo is
>     x : Integer := 42;
> begin
>      ...
> end
> 
> 
> Now foo depends on the hardwired '42' (as is should, one may argue :) 
> ).  And we are not even talking about "classes" or Lisp here.
> 
> Have I boiled down to the essentials?  

Yes.  Notably if, depending on the language, 42 is an object, and you 
may have a reason to run (foo) with a different object (either 33, or a 
(Debugging-Integer 42) or a (Bigint-Optimized-Integer 42).


Fundamentally, the direct dependency of user -> used is not a problem, 
since you can always modify the source if you want to change the dependency.

So the point of dependency injection is to be able to change this 
dependency without changing the source.  This need occurs notably when 
you want to run unit tests, ie. you want to test a single class (unit) 
without using the other classes.  Instead, you will provide stub 
implementations of the other classes.

Ie. in practice, you will have:

(defvar *used-stuff-called* nil)
(defclass stub-used (used)
   ())

(defmethod used-stuff ((self variant-used))
   (setf *used-stuff-called* t)
   'stuff)

(defmethod test/user ((self client))
   (let ((user (make-instance 'user :used (make-instance 'stub-used)))
         (*used-stuff-called* nil))
     (assert (eq 'stuff (user-stuff user)))
     (assert *used-stuff-called*)))



> How do you do the rolling eyes 
> emoticon?

🙄

> 
> All the best
> 
> Marco
> 
> 
> On Sat, Feb 6, 2021 at 10:50 PM Pascal Bourguignon 
> <pjb at informatimago.com <mailto:pjb at informatimago.com>> wrote:
> 
>     Le 06/02/2021 à 22:37, Manfred Bergmann a écrit :
>      > You have a class. This class uses some other class.
>      > But by using or creating an instance of this other class directly
>     you create a dependency on something concrete.
>      > That’s not what you want, because you might want to replace this
>     with something else if required. For example with a mock or fake
>     implementation in a test.
>      > ‚Dependency injection‘ allows you to declare this dependency with
>     just an interface/protocol and have some other facility (the
>     dependency injection framework) ‚inject‘ a concrete object at run-time.
>      > A similar thing could certainly be done by just using a
>     constructor parameter (strategy pattern).
>      > But I think the important part here is the dependency on just an
>     interface and not on a concrete implementation. For flexibility.
> 
> 
>     With some code:
> 
>     ;;;------------------------------------------------------------
> 
>     (defclass used ()
>         ())
> 
>     (defmethod used-stuff ((self used))
>         'stuff)
> 
>     ;;; ---
> 
>     (defclass user ()
>         ((used :reader used)))
> 
>     (defmethod initialize-instance :after ((self user) &key
>     &allow-other-keys)
>         (setf (slot-value self 'used) (make-instance 'used #|OOPS,
>     Dependency!|#)))
> 
>     (defmethod user-stuff ((self user))
>         ;; Not a real dependency on the used class,
>         ;; it's a dependency on the used-stuff generic function (interface).
>         (used-stuff (used self)))
> 
>     ;;; ---
> 
>     (defclass client ()
>         ())
> 
>     (defmethod create-user ((self client))
>         ;; The class client depends directly on the user class,
>         ;; and indirectly on the used class.
>         (make-instance 'user))
> 
> 
>     ;;;------------------------------------------------------------
> 
>     (defclass used ()
>         ())
> 
>     (defmethod used-stuff ((self used))
>         'stuff)
> 
>     ;;; ---
> 
>     (defclass user ()
>         ((used :initarg :used :reader used)))
> 
>     ;; The user class has no more any dependency on the used class.
> 
>     (defmethod user-stuff ((self user))
>         ;; Not a real dependency on the used class,
>         ;; it's a dependency on the used-stuff generic function (interface).
>         (used-stuff (used self)))
> 
>     ;;; ---
> 
>     (defclass client ()
>         ())
> 
>     (defmethod create-user ((self client))
>         ;; The class client depends explicitely on the user and used
>     classes.
>         ;; But now, the class user doesn't depend directly on the used
>     class;
>         ;; this dependency is injected by the client into the user classe:
>         (make-instance 'user :used (make-instance 'used)))
> 
> 
>     ;;;------------------------------------------------------------
> 
>     ;; Notably if the client wants the user to use another used class:
> 
>     (defclass variant-used (used)
>         ())
>     (defmethod used-stuff ((self variant-used))
>         'variant-stuff)
> 
>     (defmethod create-user ((self client))
>         ;; only the client needs to be changed; the user class won't know
>         ;; the difference:
>         (make-instance 'user :used (make-instance 'variant-used)))
> 
> 
>     -- 
>     __Pascal Bourguignon__
> 
> 
> 
> -- 
> Marco Antoniotti, Associate Professortel.+39 - 02 64 48 79 01
> DISCo, Università Milano Bicocca U14 2043http://bimib.disco.unimib.it 
> <http://bimib.disco.unimib.it/>
> Viale Sarca 336
> I-20126 Milan (MI) ITALY


-- 
__Pascal Bourguignon__



More information about the pro mailing list