Am 07.02.2021 um 09:25 schrieb Hans Hübner hans.huebner@gmail.com:
The applicability of dependency injection really depends on the complexity of the framework that is being talked about. In simple, and in particular in framework-less systems, one does not really need a framework-specific, configurable, run-time linking component. Most Lisp systems do not actually depend on a framework, and Lisp - as a dynamic language - provides for plenty of run-time features that can be used to achieve what is the magic sauce in dependency injection frameworks. One may want to standardize how component linkage is performed in a large Lisp system, of course, but the challenges would be different as much of the tooling to perform the actual work is available right away.
Well. There are plenty libraries out there for Common Lisp. The question one has to ask. Do I use them directly, creating a source code dependency, like a web server. Or do create and use an abstraction in order to be able to replace it. In the sense of ‚keeping libraries at arms length‘. For small application this is of course overkill. But I would be interested what the alternatives in Lisp are. In some Common Lisp project I did last year I had separated the protocols from the implementations. Structured the ASDF loading in a sequence that the protocols are loaded first, then a layer that uses those protocols is loaded, and finally the concrete implementations are loaded. This worked fine. But I just came up with this and I’m not sure if there are better alternatives.
The same can be said about many of the patterns that are used in the OO and Java world, of course. They often provide solution architectures to problems that are hard to solve given the constraints of - in particular - Java, and for which trivially easy solutions can be devised in Lisp on the fly without having to resort to a named pattern.
That’s what I’m trying to figure out. Implementing Abstract Factory in Java (or Scala for what I do) is relatively bloated. We figured so far that make-instance `clazz-symbol is probably the simplified version to do that in Common Lisp, with maybe a little bit of boiler-plate.
Manfred
Am So., 7. Feb. 2021 um 08:59 Uhr schrieb Marco Antoniotti marco.antoniotti@unimib.it: 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? How do you do the rolling eyes emoticon?
All the best
Marco
On Sat, Feb 6, 2021 at 10:50 PM Pascal Bourguignon pjb@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 Professor tel. +39 - 02 64 48 79 01 DISCo, Università Milano Bicocca U14 2043 http://bimib.disco.unimib.it Viale Sarca 336 I-20126 Milan (MI) ITALY