Digamos que tengo una función IsAdmin
que verifica si un usuario es administrador. Supongamos también que la comprobación del administrador se realiza haciendo coincidir el ID de usuario, el nombre y la contraseña con algún tipo de regla (no importante).
En mi cabeza, hay dos posibles firmas de funciones para esto:
public bool IsAdmin(User user);
public bool IsAdmin(int id, string name, string password);
A menudo busco el segundo tipo de firma, pensando que:
- La firma de la función le da al lector mucha más información
- La lógica contenida dentro de la función no tiene que saber sobre la
User
clase - Por lo general, resulta un poco menos de código dentro de la función
Sin embargo, a veces cuestiono este enfoque y también me doy cuenta de que en algún momento sería difícil de manejar. Si, por ejemplo, una función mapeara entre diez campos de objetos diferentes en un bool resultante, obviamente enviaría todo el objeto. Pero aparte de un claro ejemplo como ese, no puedo ver una razón para pasar el objeto real.
Agradecería cualquier argumento para cualquier estilo, así como cualquier observación general que pueda ofrecer.
Programo tanto en estilos orientados a objetos como en estilos funcionales, por lo que la pregunta debe considerarse en relación con todos y cada uno de los modismos.
fuente
Respuestas:
Personalmente prefiero el primer método de solo
IsAdmin(User user)
Es mucho más fácil de usar, y si sus criterios para IsAdmin cambian en una fecha posterior (tal vez en función de los roles o isActive), no necesita volver a escribir la firma de su método en todas partes.
Probablemente también sea más seguro, ya que no anuncia qué propiedades determinan si un usuario es administrador o no, o si transfiere la propiedad de contraseña a todas partes. Y syrion señala que lo que sucede cuando tu
id
no coincide con elname
/password
?La longitud del código dentro de una función realmente no debería importar si el método hace su trabajo, y preferiría tener un código de aplicación más corto y simple que un código de método auxiliar.
fuente
La primera firma es superior porque permite la encapsulación de la lógica relacionada con el usuario en el
User
objeto mismo. No es beneficioso tener lógica para la construcción de la tupla de "id, nombre, contraseña" esparcida sobre su base de código. Además, complica laisAdmin
función: ¿qué sucede si alguien pasa en unaid
que no coincide conname
, por ejemplo? ¿Qué sucede si desea verificar si un usuario determinado es un administrador de un contexto en el que no debe conocer su contraseña?Además, como una nota sobre su tercer punto a favor del estilo con argumentos adicionales; puede resultar en "menos código dentro de la función", pero ¿a dónde va esa lógica? ¡No puede desaparecer! En cambio, se extiende por todos los lugares donde se llama la función. Para guardar cinco líneas de código en un solo lugar, ¡ha pagado cinco líneas por uso de la función!
fuente
user.IsAdmin()
sería mucho mejor?IsAdmin
una forma o función específica, eso no debería estar relacionado con el usuarioEl primero, pero no (solo) por las razones que otros han dado.
Esto es de tipo seguro. Especialmente porque el usuario es un tipo que usted definió, hay poca o ninguna oportunidad de transponer o cambiar argumentos. Claramente opera en los usuarios y no es una función genérica que acepte cualquier int y dos cadenas. Esta es una gran parte del objetivo de usar un lenguaje de tipo seguro como Java o C #, como se ve su código. Si está preguntando sobre un idioma específico, es posible que desee agregar una etiqueta para ese idioma a su pregunta.
Aquí, cualquier int y dos cadenas servirá. ¿Qué le impide transponer el nombre y la contraseña? El segundo es más trabajo para usar y permite más oportunidades de error.
Presumiblemente, ambas funciones requieren que se llame a user.getId (), user.getName () y user.getPassword (), ya sea dentro de la función (en el primer caso) o antes de llamar a la función (en el segundo), entonces La cantidad de acoplamiento es la misma en ambos sentidos. En realidad, dado que esta función solo es válida en Usuarios, en Java eliminaría todos los argumentos y haría de este un método de instancia en objetos de Usuario:
Dado que esta función ya está estrechamente vinculada a los usuarios, tiene sentido que sea parte de lo que es un usuario.
PD: Estoy seguro de que esto es solo un ejemplo, pero parece que está almacenando una contraseña. En su lugar, solo debe almacenar un hash de contraseña criptográficamente seguro. Durante el inicio de sesión, la contraseña suministrada se debe cifrar de la misma manera y comparar con el hash almacenado. Se debe evitar enviar contraseñas en texto plano. Si viola esta regla e isAdmin (int Str Str) registrara el nombre de usuario, entonces si transpuso el nombre y la contraseña en su código, la contraseña podría registrarse en su lugar, lo que crea un problema de seguridad (no escriba contraseñas en los registros) ) y es otro argumento a favor de pasar un objeto en lugar de sus partes componentes (o usar un método de clase).
fuente
Si su lenguaje de elección no pasa estructuras por valor, entonces la
(User user)
variante parece mejor. ¿Qué sucede si algún día decide eliminar el nombre de usuario y solo usa la ID / contraseña para identificar al usuario?Además, este enfoque inevitablemente conduce a llamadas de método largas y exageradas, o inconsistencias. ¿Está bien pasar 3 argumentos? ¿Qué tal 5? O 6? ¿Por qué pensar en eso, si pasar un objeto es barato en términos de recursos (incluso más barato que pasar 3 argumentos, probablemente)?
Y estoy (en cierto modo) en desacuerdo con que el segundo enfoque le da al lector más información: intuitivamente, la llamada al método pregunta "si el usuario tiene derechos de administrador", no "si la combinación de identificación / nombre / contraseña dada tiene derechos de administrador".
Entonces, a menos que pasar el
User
objeto resulte en copiar una estructura grande, el primer enfoque es más limpio y más lógico para mí.fuente
Probablemente tendría ambos. El primero como API externo, con alguna esperanza de estabilidad en el tiempo. El segundo como una implementación privada llamada por la API externa.
Si en algún momento posterior tuviera que cambiar la regla de verificación, simplemente escribiría una nueva función privada con una nueva firma llamada por la externa.
Esto tiene la ventaja de la facilidad de cambiar la implementación interna. También es muy habitual que en algún momento pueda necesitar ambas funciones disponibles al mismo tiempo y llamar a una u otra dependiendo de algún cambio de contexto externo (por ejemplo, puede tener usuarios antiguos coexistentes y usuarios nuevos con diferentes campos).
Con respecto al título de la pregunta, de alguna manera, en ambos casos, está proporcionando la información mínima necesaria. Un usuario parece ser la estructura de datos relacionados con la aplicación más pequeña que contiene la información. Los tres campos id, contraseña y nombre parecen ser los datos de implementación más pequeños realmente necesarios, pero en realidad no son objetos de nivel de aplicación.
En otras palabras, si se tratara de una base de datos, un Usuario sería un registro, mientras que el ID, la contraseña y el inicio de sesión serían campos en ese registro.
fuente
Hay un punto negativo para enviar clases (tipos de referencia) a los métodos, y es la vulnerabilidad de asignación masiva . Imagina que en lugar de
IsAdmin(User user)
método, tengoUpdateUser(User user)
método.Si la
User
clase tiene una propiedad booleana llamadaIsAdmin
, y si no la verifico durante la ejecución de mi método, entonces soy vulnerable a la asignación masiva. GitHub fue atacado por esta misma firma en 2012.fuente
Yo iría con este enfoque:
El uso del tipo de interfaz como parámetro para esta función le permite pasar objetos de usuario, definir objetos de usuario simulados para probar, y le brinda más flexibilidad en cuanto al uso y mantenimiento de esta función.
También agregué la palabra clave
this
para hacer que la función sea un método de extensión de todas las clases de IUser; de esta manera, puede escribir código de una manera más amigable para OO y usar esta función en consultas de Linq. Aún más simple sería definir esta función en IUser y User, pero supongo que hay alguna razón por la que decidió poner este método fuera de esa clase.Utilizo la interfaz personalizada en
IID
lugar de,int
ya que esto le permite redefinir las propiedades de su ID; por ejemplo, en caso de que alguna vez tiene que cambiar deint
along
,Guid
o algo más. Probablemente sea una exageración, pero siempre es bueno tratar de flexibilizar las cosas para que no te quedes atrapado en decisiones tempranas.NB: su objeto IUser puede estar en un espacio de nombres y / o ensamblado diferente a la clase de usuario, por lo que tiene la opción de mantener su código de usuario completamente separado de su código IsAdmin, con ellos solo compartiendo una biblioteca referenciada. Exactamente lo que haces es una llamada sobre cómo sospechas que se usarán estos artículos.
fuente
Descargos de responsabilidad:
Para mi respuesta, me centraré en el tema del estilo y olvidaré si un ID, inicio de sesión y contraseña simple es un buen conjunto de parámetros para usar, desde el punto de vista de la seguridad. Debería poder extrapolar mi respuesta a cualquier conjunto básico de datos que esté utilizando para descubrir los privilegios del usuario ...
También considero
user.isAdmin()
que es equivalente aIsAdmin(User user)
. Esa es una decisión que debes tomar.Respuesta:
Mi recomendación es para cualquier usuario solamente:
O use ambos, a lo largo de las líneas de:
Razones para el método público:
Al escribir métodos públicos, generalmente es una buena idea pensar cómo se utilizarán.
Para este caso en particular, la razón por la que generalmente llamaría a este método es para averiguar si cierto usuario es un administrador. Ese es un método que necesitas. Realmente no desea que cada persona que llama tenga que elegir los parámetros correctos para enviar (y corre el riesgo de errores debido a la duplicación de código).
Razones para el método privado:
El método privado que recibe solo el conjunto mínimo de parámetros requeridos puede ser algo bueno a veces. A veces no tanto.
Una de las ventajas de dividir estos métodos es que puede llamar a la versión privada desde varios métodos públicos en la misma clase. Por ejemplo, si desea (por cualquier motivo) tener una lógica ligeramente diferente
Localuser
yRemoteUser
(¿tal vez hay una configuración para activar / desactivar los administradores remotos?):Además, si por alguna razón necesita hacer público el método privado ... puede hacerlo. Es tan fácil como cambiar
private
apublic
. Puede no parecer una gran ventaja para este caso, pero a veces lo es realmente.Además, cuando realice pruebas unitarias, podrá realizar muchas más pruebas atómicas. Por lo general, es muy bueno no tener que crear un objeto de usuario completo para ejecutar pruebas en este método. Y si desea una cobertura completa, puede probar ambas llamadas.
fuente
es malo por los motivos enumerados. Eso no quiere decir
es automáticamente bueno En particular, si el usuario es un objeto que tiene métodos como:
getOrders()
,getFriends()
,getAdresses()
, ...Puede considerar refactorizar el dominio con un tipo de credenciales de usuario que contenga solo ID, nombre, contraseña y pasarlo a IsAdmin en lugar de todos los datos de usuario innecesarios.
fuente