|
| 1 | +open Printf |
| 2 | + |
| 3 | +(******************************************************************************) |
| 4 | +(* *) |
| 5 | +(* Dependency Inversion Principle *) |
| 6 | +(* *) |
| 7 | +(* DIP is the strategy of depending upon interfaes or abstract functions *) |
| 8 | +(* and classes rather than upon concrete functions and classes. A good rule *) |
| 9 | +(* to apply is that higher-level components should not depend on a lower *) |
| 10 | +(* level component, and they should also work in isolation. DIP tells us *) |
| 11 | +(* that every dependency in the design should target an interface or an *) |
| 12 | +(* abstract class. Furthermore, a dependency should not target a concrete *) |
| 13 | +(* class. It's also worth noting that {e Dependency Injection} is just a *) |
| 14 | +(* vehicle to achieve the inversion of control. *) |
| 15 | +(* *) |
| 16 | +(******************************************************************************) |
| 17 | + |
| 18 | +module LowLevelModule = struct |
| 19 | + let do_something () = print_endline "LLM Doing something..." |
| 20 | +end |
| 21 | + |
| 22 | +module HighLevelModule = struct |
| 23 | + (** [LowLevelSubModule] is tightly coupled with the high-level module. |
| 24 | + This breaks the DIP and should be abstracted with a functor instead. *) |
| 25 | + module LowLevelSubModule = LowLevelModule |
| 26 | + |
| 27 | + let say_hello () = |
| 28 | + print_endline "Hello from HLM, submodule does something:"; |
| 29 | + LowLevelModule.do_something () |
| 30 | + ;; |
| 31 | +end |
| 32 | + |
| 33 | +let _ = HighLevelModule.say_hello () |
| 34 | + |
| 35 | +module type AbstractModule = sig |
| 36 | + val do_something : unit -> unit |
| 37 | +end |
| 38 | + |
| 39 | +module ConcreteModule : AbstractModule = struct |
| 40 | + let do_something () = print_endline "ConcreteModule does something..." |
| 41 | +end |
| 42 | + |
| 43 | +module DIP_HLM_Functor (AM : AbstractModule) : sig |
| 44 | + val say_hello : unit -> unit |
| 45 | +end = struct |
| 46 | + (** [ASM] is an injected dependency module that implements the |
| 47 | + [AbstractModule] pseudointerface.*) |
| 48 | + module ASM = AM |
| 49 | + |
| 50 | + let say_hello () = |
| 51 | + print_endline "Hello from a DIP-complied HLM!"; |
| 52 | + print_endline "The abstract submodule does something:"; |
| 53 | + ASM.do_something () |
| 54 | + ;; |
| 55 | +end |
| 56 | + |
| 57 | +module DIP_Compliant = DIP_HLM_Functor (ConcreteModule) |
| 58 | + |
| 59 | +let _ = |
| 60 | + print_newline (); |
| 61 | + (* Sealing [DIP_Compliant] with a module signature allows us to make the |
| 62 | + abstract submodule (dependency) private, just like in OOP. *) |
| 63 | + DIP_Compliant.say_hello () |
| 64 | +;; |
| 65 | + |
| 66 | +(* Here's how it's done with classes in OCaml. *) |
| 67 | + |
| 68 | +class type abstraction = object |
| 69 | + method operation : unit |
| 70 | +end |
| 71 | + |
| 72 | +class low_level_class : abstraction = |
| 73 | + object |
| 74 | + method operation = print_endline "Low-level class operation/method" |
| 75 | + end |
| 76 | + |
| 77 | +class high_level_class (dependency' : abstraction) = |
| 78 | + object |
| 79 | + val dependency = dependency' |
| 80 | + |
| 81 | + method use_dependency = |
| 82 | + print_endline "High level class uses its injected dependency:"; |
| 83 | + dependency#operation |
| 84 | + end |
| 85 | + |
| 86 | +let _ = |
| 87 | + let hlo = new high_level_class (new low_level_class) in |
| 88 | + print_newline (); |
| 89 | + hlo#use_dependency |
| 90 | +;; |
| 91 | + |
| 92 | +(* |
| 93 | + DIFICULTAD EXTRA (opcional): |
| 94 | + Crea un sistema de notificaciones. |
| 95 | + Requisitos: |
| 96 | + 1. El sistema puede enviar Email, PUSH y SMS (implementaciones específicas). |
| 97 | + 2. El sistema de notificaciones no puede depender de las implementaciones específicas. |
| 98 | + Instrucciones: |
| 99 | + 1. Crea la interfaz o clase abstracta. |
| 100 | + 2. Desarrolla las implementaciones específicas. |
| 101 | + 3. Crea el sistema de notificaciones usando el DIP. |
| 102 | + 3. Desarrolla un código que compruebe que se cumple el principio. |
| 103 | +*) |
| 104 | + |
| 105 | +module type Notifier = sig |
| 106 | + val id : string |
| 107 | + val notify : string -> string -> unit |
| 108 | +end |
| 109 | + |
| 110 | +module EmailService : Notifier = struct |
| 111 | + |
| 112 | + let id = "EMAILER" |
| 113 | + |
| 114 | + let notify msg destination = |
| 115 | + print_endline "Email sent:\n--------------------------"; |
| 116 | + printf "From: %s\n" address; |
| 117 | + printf "To: %s\n" destination; |
| 118 | + print_endline msg |
| 119 | + ;; |
| 120 | +end |
| 121 | + |
| 122 | +module NotificationManager (N : Notifier) : sig |
| 123 | + val create_and_send : string -> string -> unit |
| 124 | +end = struct |
| 125 | + module NotificationService = N |
| 126 | + |
| 127 | + let create_and_send msg destination = |
| 128 | + printf |
| 129 | + "Sending [%s] to [%s] via %s...\n" |
| 130 | + msg |
| 131 | + destination |
| 132 | + NotificationService.id; |
| 133 | + NotificationService.notify msg destination |
| 134 | + ;; |
| 135 | +end |
| 136 | + |
| 137 | +(* We can inject an existing module that satisfies the interface: *) |
| 138 | +module EmailNotificationManager = NotificationManager (EmailService) |
| 139 | + |
| 140 | +(* OR... we can also inject anonymous modules in-line: *) |
| 141 | +module SMSNotificationManager = NotificationManager (struct |
| 142 | + let id = "TelcelSMS" |
| 143 | + let phone_number = "664-563-7778" |
| 144 | + |
| 145 | + let notify msg destination = |
| 146 | + print_endline "SMS sent:\n--------------------------"; |
| 147 | + printf "To: [[ %s ]]\n" destination; |
| 148 | + printf "Text message: %s\n" msg; |
| 149 | + printf "From: [[ %s ]]\n" phone_number |
| 150 | + ;; |
| 151 | + end) |
| 152 | + |
| 153 | +module UbuntuNotificationCenter = NotificationManager (struct |
| 154 | + let id = "UbuntuNotifications" |
| 155 | + |
| 156 | + let notify msg destination = |
| 157 | + printf "Application: %s\n" destination; |
| 158 | + print_endline msg |
| 159 | + ;; |
| 160 | + end) |
| 161 | + |
| 162 | +let _ = |
| 163 | + print_newline (); |
| 164 | + EmailNotificationManager.create_and_send |
| 165 | + "Hello, how are you? Let's meet soon" |
| 166 | + |
| 167 | + print_newline (); |
| 168 | + SMSNotificationManager.create_and_send "Did you buy milk?" "664-121-4442"; |
| 169 | + print_newline (); |
| 170 | + UbuntuNotificationCenter.create_and_send |
| 171 | + "Currently playing: Los Lobos - La Bamba" |
| 172 | + "Spotify" |
| 173 | +;; |
0 commit comments