Actualmente estoy pensando en una interfaz para una clase que estoy escribiendo. Esta clase contiene estilos para un carácter, por ejemplo, si el carácter está en negrita, cursiva, subrayado, etc. He estado debatiendo conmigo mismo durante dos días si debo usar getters / setters o nombres lógicos para los métodos que cambian los valores a estos estilos Si bien tiendo a preferir nombres lógicos, significa escribir código que no es tan eficiente ni lógico. Dejame darte un ejemplo.
Tengo una clase CharacterStyles
que tiene variables miembro bold
, italic
, underline
(y algunos otros, pero voy a dejar fuera de mantenerlo simple). La forma más fácil de permitir que otras partes del programa accedan a estas variables, sería escribir métodos getter / setter, para que pueda hacer styles.setBold(true)
y styles.setItalic(false)
.
Pero no me gusta esto. No solo porque mucha gente dice que los captadores / establecedores rompen la encapsulación (¿es realmente tan malo?), Sino principalmente porque no me parece lógico. Espero diseñar un personaje a través de un método, styles.format("bold", true)
o algo así, pero no a través de todos estos métodos.
Sin embargo, hay un problema. Como no puede acceder a una variable miembro del objeto por el contenido de una cadena en C ++, tendría que escribir un gran contenedor de declaración / cambio para todos los estilos, o tendría que almacenar los estilos en una matriz asociativa ( mapa).
No puedo entender cuál es la mejor manera. En un momento creo que debería escribir los captadores / establecedores, y al momento siguiente me inclino hacia el otro lado. Mi pregunta es: ¿qué harías? ¿Y por qué harías eso?
bold
set totrue
y las otras variables indefinidas, los métodos getter para las otras variables deberían devolver el valor del estilo padre (que está almacenado en otra propiedad), mientras que el getter para labold
propiedad debería regresartrue
. Hay algunas otras cosas como un nombre y la forma en que se muestra en la interfaz también.Respuestas:
Sí, los getters / setters rompen la encapsulación, básicamente son solo una capa adicional entre acceder directamente al campo subyacente. También podría acceder directamente a él.
Ahora, si desea métodos más complejos para acceder al campo, eso es válido, pero en lugar de exponer el campo, debe pensar qué métodos debería ofrecer la clase en su lugar. es decir. en lugar de una clase de banco con un valor monetario que se expone utilizando una propiedad, debe pensar qué tipos de acceso debe ofrecer un objeto del banco (agregar dinero, retirar, obtener saldo) e implementarlos en su lugar. Una propiedad en la variable Money solo es sintácticamente diferente de exponer la variable Money directamente.
DrDobbs tiene un artículo que dice más.
Para su problema, tendría 2 métodos setStyle y clearStyle (o lo que sea) que toman una enumeración de los posibles estilos. Una declaración de cambio dentro de estos métodos aplicaría los valores relevantes a las variables de clase apropiadas. De esta manera, puede cambiar la representación interna de los estilos a otra cosa si luego decide almacenarlos como una cadena (para usar en HTML, por ejemplo), algo que requeriría que todos los usuarios de su clase también cambien si usa obtener / establecer propiedades.
Aún puede activar cadenas si desea tomar valores arbitrarios, ya sea tener una gran instrucción if-then (si hay algunas) o un mapa de valores de cadena para punteros de método usando std :: mem_fun (o std :: función ) por lo que "bold" se almacenaría en una clave de mapa con su valor como sts :: mem_fun en un método que establece la variable bold en true (si las cadenas son las mismas que los nombres de las variables miembro, también puede usar la macro de cadena para reducir la cantidad de código que necesita escribir)
fuente
styles.setStyle(BOLD, true)
? ¿Es eso correcto?styles.getStyle(BOLD)
cuando no solo tiene variables miembro de tipo booleano, sino también de tipos enteros y cadenas (por ejemplo:)styles.getStyle(FONTSIZE)
. Como no puede sobrecargar los tipos de retorno, ¿cómo programa esto? Sé que podrías usar punteros vacíos, pero eso me parece una muy mala manera. ¿Tienes alguna pista sobre cómo hacer esto?Una idea que quizás no hayas considerado es el Patrón Decorador . En lugar de establecer banderas en un objeto y luego aplicar esas banderas a lo que sea que esté escribiendo, ajusta la clase que escribe en Decoradores, que a su vez aplica los estilos.
El código de llamada no necesita saber cuántos de estos envoltorios ha colocado alrededor de su texto, simplemente llama a un método en el objeto externo y llama a la pila.
Para un ejemplo de pseudocódigo:
Y así sucesivamente, para cada estilo de decoración. En su uso más simple, puede decir
Y el código que llama a este método no necesita conocer los detalles de lo que está recibiendo. Siempre que sea ALGO que pueda dibujar texto a través de un método WriteString.
fuente
TextWriter
necesite hablar con ambosBoldDecorator
yItalicDecorator
(bidireccionalmente) para hacer el trabajo.Me inclinaría hacia la segunda solución. Se ve más elegante y flexible. Aunque es difícil imaginar otros tipos que no sean negrita, cursiva y subrayado (¿sobrelínea?), Sería difícil agregar nuevos tipos utilizando variables miembro.
Utilicé este enfoque en uno de mis últimos proyectos. Tengo una clase que puede tener múltiples atributos booleanos. El número y los nombres de los atributos pueden cambiar con el tiempo. Los guardo en un diccionario. Si un atributo no está presente, supongo que su valor es "falso". También tengo que almacenar una lista de nombres de atributos disponibles, pero esa es otra historia.
fuente
Creo que estás pensando demasiado en el tema.
styles.setBold(true)
ystyles.setItalic(false)
están bienfuente