C贸digo obsoleto - C贸digo de terceros

imagen

Hay un consejo en la comunidad TDD que dice que no debemos usar objetos simulados para tipos que no son de nuestra propiedad . Creo que este es un buen consejo, y trato de seguirlo. Por supuesto, hay personas que dicen que no debemos usar objetos simulados en absoluto. No importa qu茅 opini贸n tenga, el consejo "no imitar lo que no es suyo" tambi茅n contiene un significado oculto. La gente a menudo lo pasa de largo, al ver la palabra "burlarse" y enojarse.

Este significado oculto es que debe crear interfaces, clientes, puentes, adaptadores entre nuestra aplicaci贸n y el c贸digo de terceros que utilizamos. Si crearemos objetos simulados de estas interfaces en nuestras pruebas ni siquiera es tan importante. Lo importante es que creamos y usamos interfaces que separan mejor nuestro c贸digo del c贸digo de terceros. Un ejemplo cl谩sico de esto en el mundo PHP es crear y usar un cliente HTTP en nuestra aplicaci贸n que use el cliente Guzzle HTTP , en lugar de usar Guzzle directamente.

Por qu茅 Bueno, para empezar, Guzzle tiene una API mucho m谩s poderosa que la que necesita su aplicaci贸n (en la mayor铆a de los casos). La creaci贸n de su propio cliente HTTP, que proporciona solo el conjunto necesario de API de Guzzle, limitar谩 a los desarrolladores de aplicaciones a lo que pueden hacer con este cliente. Si la API de Guzzle cambia en el futuro, tendremos que hacer cambios en un solo lugar, en lugar de corregir sus llamadas en toda la aplicaci贸n con la esperanza de que nada se rompa. 隆Dos muy buenas razones, y ni siquiera mencion茅 objetos simulados!

No creo que esto sea dif铆cil de lograr. El c贸digo de terceros generalmente se encuentra en una carpeta separada de nuestra aplicaci贸n, a menudo es vendor/ o library/ . Tambi茅n reside en un espacio de nombres diferente y tiene una convenci贸n de nomenclatura diferente a la que se usa en nuestra aplicaci贸n. El c贸digo de terceros es bastante f谩cil de identificar y, con un poco de disciplina, podemos hacer que nuestro c贸digo de aplicaci贸n sea menos dependiente de partes de terceros.

驴Qu茅 sucede si aplicamos las mismas reglas al c贸digo heredado?


驴Qu茅 sucede si miramos nuestro c贸digo heredado y el de terceros? Esto puede ser dif铆cil de hacer, o incluso contraproducente si el c贸digo obsoleto se usa exclusivamente en modo de soporte, cuando solo corregimos errores y ajustamos un poco peque帽as partes de 茅l. Pero si escribimos un nuevo c贸digo que (re) usa obsoleto, creo que vale la pena considerarlo de la misma manera que el c贸digo de terceros. Al menos desde el punto de vista del nuevo c贸digo.

Si es posible, el c贸digo obsoleto y nuevo debe ubicarse en diferentes carpetas y espacios de nombres. Ha pasado mucho tiempo desde la 煤ltima vez que vi el sistema sin inicio, por lo que es bastante factible. Pero en lugar de usar ciegamente c贸digo heredado en el nuevo c贸digo, 驴qu茅 pasa si creamos interfaces para 茅l y los usamos?

El c贸digo obsoleto a menudo est谩 lleno de objetos "divinos" que hacen demasiadas cosas. Usan un estado global, tienen propiedades p煤blicas o m茅todos m谩gicos que dan acceso a propiedades privadas como si fueran p煤blicas, tienen m茅todos est谩ticos que son muy convenientes para llamar a cualquier persona y en cualquier lugar. As铆 que esta conveniencia nos ha llevado a la situaci贸n en la que nos encontramos.

Otro problema, quiz谩s incluso m谩s grave, con el c贸digo desactualizado es que estamos listos para cambiarlo, arreglarlo, descifrarlo porque no lo consideramos como c贸digo de terceros. 驴Qu茅 hacemos cuando vemos un error o queremos agregar una nueva funci贸n al c贸digo de terceros? Describimos el problema y / o creamos una solicitud de extracci贸n. Lo que no hacemos es no ir al vendor/ carpeta y no editar el c贸digo all铆. 驴Por qu茅 estamos haciendo esto con c贸digo heredado? Y luego cruzamos nuestros dedos y esperamos que nada se haya roto.

En lugar de utilizar ciegamente c贸digo obsoleto en el nuevo c贸digo, intentemos escribir interfaces que incluyan solo el subconjunto requerido de la API del antiguo objeto "divino". Digamos que tenemos un objeto User en c贸digo heredado que sabe todo sobre todo. Sabe c贸mo cambiar el correo electr贸nico y la contrase帽a, c贸mo actualizar los usuarios del foro a moderadores, c贸mo actualizar los perfiles de usuarios p煤blicos, establecer las preferencias de notificaci贸n, guardarse en la base de datos y mucho m谩s.

src / Legacy / User.php
 <?php namespace Legacy; class User { public $email; public $password; public $role; public $name; public function promote($newRole) { $this->role = $newRole; } public function save() { db_layer::save($this); } } 

Este es un ejemplo crudo, pero refleja el problema: cada propiedad es p煤blica y se puede cambiar f谩cilmente a cualquier valor, debemos recordar llamar expl铆citamente al m茅todo save despu茅s de cualquier cambio para guardar, etc.

Limit茅monos y proh铆bamos el acceso a estas propiedades p煤blicas e intentemos adivinar c贸mo funciona un sistema desactualizado mientras aumentamos los derechos de los usuarios:

src / LegacyBridge / Promoter.php
 <?php namespace LegacyBridge; interface Promoter { public function promoteTo(Role $role); } 

src / LegacyBridge / LegacyUserPromoter.php
 <?php namespace LegacyBridge; class LegacyUserPromoter implements Promoter { private $legacyUser; public function __construct(Legacy\User $user) { $this->legacyUser = $user; } public function promoteTo(Role $newRole) { $newRole = (string) $newRole; //  ,  $role     ?  ! $legacyRoles = [ Role::MODERATOR => 1, Role::MEMBER => 2, ]; $newLegacyRole = $legacyRoles[$newRole]; $this->legacyUser->promote($newLegacyRole); $this->legacyUser->save(); } } 

Ahora, cuando queremos aumentar los derechos de User en el nuevo c贸digo, utilizamos la interfaz LegacyBridge\Promoter , que se ocupa de todas las sutilezas de aumentar un usuario en un sistema desactualizado.

Cambiar idioma de herencia


La interfaz para el c贸digo obsoleto nos brinda la oportunidad de mejorar el dise帽o del sistema y puede salvarnos de posibles errores de nombres que se cometieron hace mucho tiempo. El proceso de cambiar el rol de un usuario de moderador a participante no es "promoci贸n" (aumento), sino m谩s bien "degradaci贸n" (disminuci贸n). Nadie nos impide crear dos interfaces para estas cosas diferentes, incluso si el c贸digo heredado se ve igual.

src / LegacyBridge / Promoter.php
 <?php namespace LegacyBridge; interface Promoter { public function promoteTo(Role $role); } 

src / LegacyBridge / LegacyUserPromoter.php
 <?php namespace LegacyBridge; class LegacyUserPromoter implements Promoter { private $legacyUser; public function __construct(Legacy\User $user) { $this->legacyUser = $user; } public function promoteTo(Role $newRole) { if ($newRole->isMember()) { throw new \Exception("Can't promote to a member."); } $legacyMemberRole = 2; $this->legacyUser->promote($legacyMemberRole); $this->legacyUser->save(); } } 

src / LegacyBridge / Demoter.php
 <?php namespace LegacyBridge; interface Demoter { public function demoteTo(Role $role); } 

src / LegacyBridge / LegacyUserDemoter.php
 <?php namespace LegacyBridge; class LegacyUserDemoter implements Demoter { private $legacyUser; public function __construct(Legacy\User $user) { $this->legacyUser = $user; } public function demoteTo(Role $newRole) { if ($newRole->isModerator()) { throw new \Exception("Can't demote to a moderator."); } $legacyModeratorRole = 1; $this->legacyUser->promote($legacyModeratorRole); $this->legacyUser->save(); } } 


No es un gran cambio, pero el prop贸sito del c贸digo es mucho m谩s claro.

Ahora, la pr贸xima vez que necesite llamar a algunos m茅todos desde un c贸digo desactualizado, intente crear una interfaz para ellos. Puede ser imposible, puede ser demasiado costoso. S茅 que el m茅todo est谩tico de este objeto "divino" es realmente muy simple de usar y con su ayuda puedes hacer el trabajo mucho m谩s r谩pido, pero al menos considera esta opci贸n. Simplemente puede mejorar ligeramente el dise帽o del nuevo sistema que est谩 creando.

Source: https://habr.com/ru/post/es417683/


All Articles