|
| 1 | +(******************************************************************************) |
| 2 | +(* *) |
| 3 | +(* Interface Segregation Principle *) |
| 4 | +(* *) |
| 5 | +(* The core concept of this principle (the 'I' in {b SOLID}) is, a class *) |
| 6 | +(* should not be forced to implement methods that it doesn't need, caused *) |
| 7 | +(* by the implementation of a broad interface, or inheriting from a general *) |
| 8 | +(* abstract class that covers more ground than it should. *) |
| 9 | +(* *) |
| 10 | +(* The proposed solution is to create smaller interfaces that are only im- *) |
| 11 | +(* plemented if the class needs those methods and/or members and only *) |
| 12 | +(* inherit from a base class that won't force unwanted implementations. It *) |
| 13 | +(* goes hand in hand with the Liskov-Substitution Principle as it helps *) |
| 14 | +(* reduce the amount of exceptions thrown. *) |
| 15 | +(* *) |
| 16 | +(******************************************************************************) |
| 17 | + |
| 18 | +open Printf |
| 19 | + |
| 20 | +(* This interface violates the ISP because if implemented by a specific type of |
| 21 | + coffee machine, it will be forced to implement methods that it doesn't need |
| 22 | + and thus it'll need to either throw an exception or provide a NoOP body. *) |
| 23 | +class type coffee_machine_violating_isp = object |
| 24 | + method drip_brew : unit |
| 25 | + method replace_filter : unit |
| 26 | + method steam_milk : unit |
| 27 | + method pressure_brew : unit |
| 28 | + method add_ground_coffee : unit |
| 29 | +end |
| 30 | + |
| 31 | +class type coffee_machine = object |
| 32 | + method brew : unit |
| 33 | + method add_ground_coffee : unit |
| 34 | +end |
| 35 | + |
| 36 | +class type drip = object |
| 37 | + (* Inheriting a class type from another class type in OCaml is the same |
| 38 | + as extending an interface in OOP languages. Though we can't implement |
| 39 | + multiple interfaces, we can in fact inherit from multiple classes! *) |
| 40 | + inherit coffee_machine |
| 41 | + method drip_brew : unit |
| 42 | + method replace_filter : unit |
| 43 | +end |
| 44 | + |
| 45 | +class type espresso = object |
| 46 | + inherit coffee_machine |
| 47 | + method steam_milk : unit |
| 48 | + method pressure_brew : unit |
| 49 | +end |
| 50 | + |
| 51 | +class drip_machine : drip = |
| 52 | + object (self) |
| 53 | + method private drip_brew = print_endline "Brewing through a paper filter..." |
| 54 | + |
| 55 | + method add_ground_coffee = |
| 56 | + print_endline "Adding ground coffee to drip coffee machine..." |
| 57 | + |
| 58 | + method replace_filter = print_endline "Replacing paper filter..." |
| 59 | + method brew = self#drip_brew |
| 60 | + end |
| 61 | + |
| 62 | +class espresso_machine : espresso = |
| 63 | + object (self) |
| 64 | + method private pressure_brew = |
| 65 | + print_endline "Brewing through an espresso portafilter..." |
| 66 | + |
| 67 | + method add_ground_coffee = |
| 68 | + print_endline |
| 69 | + "Adding and compressing ground coffee to espresso machine..." |
| 70 | + |
| 71 | + method steam_milk = print_endline "Steaming milk for latte art..." |
| 72 | + method brew = self#pressure_brew |
| 73 | + end |
| 74 | + |
| 75 | +let _ = |
| 76 | + let oster_drip : drip_machine = new drip_machine in |
| 77 | + let barista : espresso_machine = new espresso_machine in |
| 78 | + oster_drip#add_ground_coffee; |
| 79 | + oster_drip#brew; |
| 80 | + oster_drip#replace_filter; |
| 81 | + barista#add_ground_coffee; |
| 82 | + barista#brew; |
| 83 | + barista#steam_milk |
| 84 | +;; |
| 85 | + |
| 86 | +(******************************************************************************) |
| 87 | +(* *) |
| 88 | +(* Dificultad Extra (Opcional) *) |
| 89 | +(* *) |
| 90 | +(* Crea un gestor de impresoras. *) |
| 91 | +(* Requisitos: *) |
| 92 | +(* 1. Algunas impresoras solo imprimen en blanco y negro. *) |
| 93 | +(* 2. Otras solo a color. *) |
| 94 | +(* 3. Otras son multifunción, pueden imprimir, escanear y enviar fax. *) |
| 95 | +(* Instrucciones: *) |
| 96 | +(* 1. Implementan el sistema, con los diferentes tipos de impresoras y fun- *) |
| 97 | +(* ciones. *) |
| 98 | +(* 2. Aplica el ISP a la implementación. *) |
| 99 | +(* 3. Desarrolla un código que compruebe que se cumple el principio. *) |
| 100 | +(* *) |
| 101 | +(******************************************************************************) |
| 102 | + |
| 103 | +class virtual black_white_printing = |
| 104 | + object |
| 105 | + method virtual print_b_and_w : string -> unit |
| 106 | + end |
| 107 | + |
| 108 | +class virtual color_printing = |
| 109 | + object |
| 110 | + method virtual print_color : string -> unit |
| 111 | + end |
| 112 | + |
| 113 | +class virtual fax (number : string) = |
| 114 | + object |
| 115 | + val phone_number = number |
| 116 | + method virtual send_document : string -> string -> unit |
| 117 | + end |
| 118 | + |
| 119 | +class virtual scanner = |
| 120 | + object |
| 121 | + method virtual scan_document : string -> unit |
| 122 | + end |
| 123 | + |
| 124 | +class bw_printer = |
| 125 | + object |
| 126 | + inherit black_white_printing |
| 127 | + |
| 128 | + method print_b_and_w docfile = |
| 129 | + printf "Printing [%s] in black and white...\n" docfile |
| 130 | + end |
| 131 | + |
| 132 | +class color_printer = |
| 133 | + object |
| 134 | + inherit color_printing |
| 135 | + |
| 136 | + method print_color docfile = |
| 137 | + printf "Printing [%s] in CYMK color...\n" docfile |
| 138 | + end |
| 139 | + |
| 140 | +class multifunctional_printer (number : string) = |
| 141 | + object (self) |
| 142 | + (* As I mentioned before, OCaml's class system offers multiple inheritance |
| 143 | + but I could have also made them interfaces (class types) and in order to |
| 144 | + create an interface that 'implements' all of them we could have done: |
| 145 | +
|
| 146 | + {[ |
| 147 | + class multifunctional_printer (number : string) : object |
| 148 | + inherit color_printing |
| 149 | + inherit black_white_printing |
| 150 | + inherit scanner |
| 151 | + inherit fax |
| 152 | + end = object (self) end |
| 153 | + ]} |
| 154 | +
|
| 155 | + This way we'd be forced to implement all of those methods but with one |
| 156 | + catch: using class types 'seals' a class, meaning we can't expose more |
| 157 | + public methods other than the ones in the interface. |
| 158 | + *) |
| 159 | + inherit color_printing |
| 160 | + inherit black_white_printing |
| 161 | + inherit scanner |
| 162 | + inherit fax number |
| 163 | + |
| 164 | + method scan_document docfile = |
| 165 | + printf "Scanning [%s] with a multifunctional\n" docfile |
| 166 | + |
| 167 | + method send_document docfile destination_number = |
| 168 | + printf |
| 169 | + "Sending [%s] from #%s to fax #%s through a multifunctional\n" |
| 170 | + docfile |
| 171 | + phone_number |
| 172 | + destination_number |
| 173 | + |
| 174 | + method private print_b_and_w docfile = |
| 175 | + printf |
| 176 | + "Printing [%s] in black and white using a multifunctional\n" |
| 177 | + docfile |
| 178 | + |
| 179 | + method private print_color docfile = |
| 180 | + printf "Printing [%s] in CYMK color using a multifunctional\n" docfile |
| 181 | + |
| 182 | + method print ?(use_color = true) docfile = |
| 183 | + if use_color then self#print_color docfile else self#print_b_and_w docfile |
| 184 | + end |
| 185 | + |
| 186 | +let _ = |
| 187 | + let compaq_bw = new bw_printer in |
| 188 | + let epson_color = new color_printer in |
| 189 | + let epson_mf = new multifunctional_printer "664-934-1295" in |
| 190 | + compaq_bw#print_b_and_w "2024report.xlsx"; |
| 191 | + epson_color#print_color "wedding_album.pdf"; |
| 192 | + epson_mf#print ~use_color:false "essat.docx"; |
| 193 | + epson_mf#send_document "private_letter.txt" "55-676-2424"; |
| 194 | + epson_mf#scan_document "ocr_test.jpg" |
| 195 | +;; |
0 commit comments