¿Hay una buena razón para hacer que las funciones puras no sean públicas?

25

Tuve un pequeño debate con un compañero de trabajo. En pocas palabras, ¿hay una buena razón para ocultar / encapsular funciones que son puras?

Por "puro" me refiero a la definición de Wikipedia :

  • Siempre devuelve los mismos resultados de la misma entrada. (En aras de esta discusión Foo Create(){ return new Foo(); }se considera impuro si Foono tiene semántica de valor).
  • No utiliza estado mutable (excepto variables locales) o E / S.
  • No produce efectos secundarios.
Telastyn
fuente
26
Podría haber un argumento para no hacer que tal función sea miembro de una clase.
Pieter B
66
@PieterB: de hecho, aunque no todos los idiomas admiten eso, y algunos idiomas permiten el módulo interno incluso para funciones gratuitas.
Telastyn
3
¿Cuál fue tu opinión? No creo que la pureza de la función esté relacionada con si pertenece a la API pública.
Andres F.
@andresF. - el debate se centró realmente en si una regla de validación independiente debería hacerse pública. Argumenté que, dado que era una función pura, había poco daño. Para este caso en particular, ayudó a la capacidad de prueba y era probable que se reutilizara. Esta pregunta es más sobre qué tan ampliamente podría / debería aplicarse ese argumento.
Telastyn
2
@Telastyn Si es reutilizable pero no es parte de la responsabilidad de la clase actual, probablemente debería estar en una clase / módulo / cualquiera que sea su idioma. Luego, siendo parte de la responsabilidad de la nueva clase / módulo, necesariamente se haría pública. Como mencionas las pruebas, notaré que no necesariamente tienes que burlarte del método cuando lo haces. Como "detalle de implementación", burlarse de él durante las pruebas ofrece pocos beneficios.
jpmc26

Respuestas:

76

Una función pura aún podría ser un detalle de implementación. Aunque la función puede no causar daño (desde el punto de vista de no romper invariantes / contratos importantes), al exponerla, tanto el autor como los usuarios de esa clase / módulo / paquete pierden. El autor pierde porque ahora no puede eliminarlo incluso si la implementación cambia y la función ya no le es útil. Los usuarios pierden porque tienen que examinar e ignorar las funciones adicionales que no son relevantes para usar la API para comprenderla.

Doval
fuente
33
+1. Los miembros públicos de su clase es su API. No se trata de "¿Me puede hacer daño si es público?", Sino de "¿Debería ser parte de la API?"
Ordous
1
Lo único que agregaría es que si comienza a aparecer funciones similares en otras ubicaciones, refactorícelas en otra clase para que varias clases puedan utilizarlas. O si eso es una preocupación para empezar, defínalo en una clase separada para empezar.
jpmc26
1
Sellaría las clases públicas que no están destinadas a extenderse también.
Den
+1 para señalar que ocultar o mostrar una función (o clase) son principalmente cuestiones de protección y mantenimiento de una API, y no están relacionadas con el tipo de código que es.
Marco
42

La pregunta es al revés.

No busca una razón para hacer que una función no sea pública. Para empezar, es una mentalidad incorrecta (en mi opinión). El razonamiento debe ser al revés.

En otras palabras, no pregunte "¿por qué lo haría privado?". Pregunte: "¿por qué debería hacerlo público?"

En caso de duda, no lo exponga. Es algo así como la navaja de afeitar de Ockham: no multiplique los derechos más allá de la necesidad.

EDITAR: Abordar los argumentos en contra presentados por @Telastyn en los comentarios (para evitar una discusión extendida allí):

Lo escuché con el tiempo, e incluso lo defendí durante bastante tiempo, pero en mi experiencia, las cosas tienden a ser demasiado privadas.

Sí, a veces es una molestia si una clase está abierta a la herencia, pero no puede anular algunos métodos privados (cuyo comportamiento le gustaría modificar).

Pero protectedsería suficiente, y todavía no es público.

Conduce a una gran cantidad de duplicación de código y gastos generales para llegar a "cosas que no deberían ser públicas" pero que de todas formas se acceden indirectamente.

Si se vuelve problemático, ¡simplemente hazlo público! Existe la necesidad de la que estaba hablando :)

Mi punto es que no deberías hacerlo por si acaso (YAGNI y todo).

Tenga en cuenta que siempre es más fácil hacer pública una función privada que devolverla a la privacidad. Es probable que este último rompa el código existente.

Konrad Morawski
fuente
Lo escuché con el tiempo, e incluso lo defendí durante bastante tiempo, pero en mi experiencia, las cosas tienden a ser demasiado privadas. Conduce a una gran cantidad de duplicación de código y gastos generales para llegar a "cosas que no deberían ser públicas" pero que de todas formas se acceden indirectamente.
Telastyn
3
@Telastyn En mi humilde opinión, parece que la aplicación sufre algunos errores de diseño; Si se encuentra inclinado a exponer un método porque necesita invocarlo a través de rutas de código discretas, probablemente sea una señal de que este método debe dividirse en su propio módulo que se puede inyectar o incluir cuando sea apropiado, especialmente si es una función pura .
leo
@leo - lo siento, no te sigo. Considere una función como entero menor que. Esta es una buena función pura que se expone específicamente porque luego se puede inyectar e incluir (o simplemente usar) cuando sea apropiado.
Telastyn
55
@Telastyn En mi opinión, ese es un síntoma de la filosofía "todo es un objeto / clase" combinada con el hecho de que algunos lenguajes OOP no tienen tipos de datos compuestos distintos de las clases. Tan pronto como alguien necesite pasar dos valores al mismo tiempo, termina escribiendo una clase, y luego cada compañero de trabajo y software de verificación de estilo le dirá que haga que esos campos sean privados.
Doval
@Telastyn Derecha. Lo que quiero decir es que si su método 'integerLessThan' comenzó como un detalle de implementación encapsulado de un método público, pero descubre que necesita invocar el método privado en otros lugares, probablemente sea una señal de que el método privado pertenece a un método diferente módulo / paquete / clase para que pueda importarse independientemente de la lógica que lo invoca. Simplemente publicitar el método tal como está significaría que el método se encuentra arbitrariamente en el primer módulo donde lo encontró útil.
leo
6

No creo que la decisión de ocultar / encapsular una función dependa de su pureza. El hecho de que una función sea pura no significa que los externos necesiten saber sobre ella. Curiosamente, si la función es pura y está destinada a ser pública, tal vez ni siquiera necesite ser un miembro de la instancia de la interfaz, tal vez sea más adecuada como estática. Pero nuevamente, todo esto depende de la intención del contrato y, en este caso, de la agrupación lógica de la funcionalidad, no de la pureza de la función.

por favor
fuente
5

Las clases deben cumplir con el Principio de responsabilidad única . Si bien una clase puede necesitar recurrir a otra funcionalidad para lograr sus objetivos, solo debe exponer funciones que son parte de su responsabilidad única.

Este es solo un ejemplo de un caso en el que la visibilidad podría causar un problema.

Considere una clase que frobnica los widgets. Tal vez como parte de su código de frobnication necesita alguna función de utilidad que analice una cadena: tal vez necesita transformar el nombre del widget de una manera que las funciones de cadena estándar no admitan.

Dado que esta es una función pura (la cadena entra, la transforma de alguna manera, devuelve una nueva cadena), podría ser pública o privada sin consecuencias. O podría?

Si lo haces público, ahora tu clase tiene dos responsabilidades: frobnicando widgets y transformando cadenas. Esto viola SRP y puede causar problemas si otras clases dependen de la función. Dado que esto es algo que cree que solo se usa internamente en la clase, quizás cambie su interfaz o visibilidad. Ahora las clases en otras partes del sistema están rotas.

Al mantener la función privada, nadie tiene la oportunidad de confiar en un código que no sea parte de la responsabilidad única de la clase.


fuente
2
Yo diría que solo debe contener funciones que son parte de su responsabilidad única, no solo exponer.
Telastyn
1
Creo que hay un área gris. ¿Realmente necesita una StringTransformerclase separada para encapsular dos o tres líneas de código que solo se usan en un lugar? Estoy de acuerdo en que una vez que el código se usa en varios lugares, es mejor separarlo en una nueva clase con una responsabilidad, pero hay una compensación.
Ciertamente. Las pautas son pautas, no reglas.
Telastyn
@snowman en non java solo haces una biblioteca de funciones.
Pieter B
@PieterB no se trata de Java v. Otra cosa. Para que sea realmente OO se necesitaría una fábrica, algunas clases abstractas, etc.