My 2c: It seems to me that too many things are getting mixed in (static/dynamic, abstract/concrete, etc). Dependency injection is only about dependencies and dependency inversion is also about abstraction. I would interpret dependency inversion as turning a private language of two parts into a public language that all parts depend on.For dependency injection, something like: A depends on B. Let's introduce a combinator C that depends on both (modified) A and B, which constructs a new object from mutually independent A and B. We can then use dependency inversion to make all of them use a public language, so that C can be reused for different As and Bs.
On Sun, Feb 7, 2021 at 9:27, Hans Hübnerhans.huebner@gmail.com wrote: DI Frameworks live on a different plane than most of the micro-patterns in the GoF class: They try to solve the problem that when using a framework, composing the components that a specific framework application needs can be difficult and complex. This is done by annotating the application component that makes use of a framework feature (using configuration files or, more recently, source code annotations), and then having the framework runtime collect the dependencies from the annotations to build the right set of components. Effectively, instead of statically linking the application to the framework, the linking is performed at run-time and, as the linking information is richer than in traditional linking systems (i.e. an application-level annotation instead of just a function name), it can do a lot of things that are typically aspects of systems written in dynamically typed programming languages. 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. 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. -Hans 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)))