Atualmente, estou pensando em uma interface para uma classe que estou escrevendo. Esta classe contém estilos para um caractere, por exemplo, se o caractere está em negrito, itálico, sublinhado, etc. Estou discutindo há dois dias se devo usar getters / setters ou nomes lógicos para os métodos que alteram os valores para esses estilos. Embora eu prefira nomes lógicos, significa escrever código que não é tão eficiente e nem tão lógico. Deixe-me lhe dar um exemplo.
Eu tenho uma classe CharacterStyles
que tem variáveis de membro bold
, italic
, underline
(e alguns outros, mas vou deixá-los fora para mantê-lo simples). A maneira mais fácil de permitir que outras partes do programa acessem essas variáveis seria escrever métodos getter / setter, para que você possa fazer styles.setBold(true)
e styles.setItalic(false)
.
Mas eu não gosto disso. Não apenas porque muitas pessoas dizem que os getters / setters quebram o encapsulamento (é realmente tão ruim assim?), Mas principalmente porque não me parece lógico. Espero estilizar um personagem através de um método, styles.format("bold", true)
ou algo assim, mas não através de todos esses métodos.
Há um problema, entretanto. Como você não pode acessar uma variável de membro do objeto pelo conteúdo de uma string em C ++, eu precisaria escrever um grande contêiner if-statement / switch para todos os estilos ou teria que armazenar os estilos em uma matriz associativa ( mapa).
Não consigo descobrir qual é o melhor caminho. Em um momento, acho que devo escrever os leitores, e no momento seguinte me inclino para o outro lado. Minha pergunta é: o que você faria? E por que você faria isso?
bold
definido comotrue
e as outras variáveis indefinidas, os métodos getter para as outras variáveis deverão retornar o valor do estilo pai (que está armazenado em outra propriedade), enquanto o getter dabold
propriedade deverá retornartrue
. Existem outras coisas como um nome e a maneira como ele é exibido na interface também.Respostas:
Sim, getters / setters quebram o encapsulamento - eles basicamente são apenas uma camada extra entre acessar diretamente o campo subjacente. Você também pode acessá-lo diretamente.
Agora, se você quiser métodos mais complexos para acessar o campo, isso é válido, mas, em vez de expor o campo, é necessário pensar em quais métodos a classe deve oferecer. ie em vez de uma classe Bank com um valor Money exposto usando uma propriedade, você precisa pensar que tipos de acesso um objeto Bank deve oferecer (adicionar dinheiro, retirar, obter saldo) e implementá-los. Uma propriedade na variável Money é apenas sintaticamente diferente de expor a variável Money diretamente.
DrDobbs tem um artigo que diz mais.
Para o seu problema, eu teria 2 métodos setStyle e clearStyle (ou o que seja) que levam uma enumeração dos estilos possíveis. Uma instrução switch dentro desses métodos aplicaria os valores relevantes às variáveis de classe apropriadas. Dessa forma, você pode alterar a representação interna dos estilos para outra, se posteriormente decidir armazená-los como uma sequência de caracteres (para uso em HTML, por exemplo) - algo que exigiria a alteração de todos os usuários de sua classe também se você usasse obter / definir propriedades.
Você ainda pode ativar as strings se desejar obter valores arbitrários, ou ter uma declaração if-then grande (se houver algumas delas) ou um mapa de valores de string para ponteiros de método usando std :: mem_fun (ou std :: função ) para que "bold" seja armazenado em uma chave de mapa com o valor sts :: mem_fun em um método que defina a variável bold como true (se as strings forem iguais aos nomes das variáveis de membro, você também poderá usar a macro stringing para reduzir a quantidade de código que você precisa escrever)
fonte
styles.setStyle(BOLD, true)
? Isso está correto?styles.getStyle(BOLD)
quando você não possui apenas variáveis de membro do tipo booleano, mas também dos tipos inteiro e string (por exemplostyles.getStyle(FONTSIZE)
:). Como você não pode sobrecarregar os tipos de retorno, como você programa isso? Eu sei que você poderia usar ponteiros nulos, mas isso me parece uma péssima maneira. Você tem alguma dica de como fazer isso?Uma ideia que você pode não ter considerado é o Padrão Decorador . Em vez de definir sinalizadores em um objeto e aplicá-los ao que você estiver escrevendo, agrupe a classe que escreve nos Decoradores, que, por sua vez, aplicam os estilos.
O código de chamada não precisa saber quantos desses wrappers você colocou em torno do seu texto, basta chamar um método no objeto externo e chamar a pilha.
Para um exemplo de pseudocódigo:
E assim por diante, para cada estilo de decoração. Em seu uso mais simples, você pode dizer
E o código que chama esse método não precisa saber os detalhes do que está recebendo. Contanto que ALGO possa desenhar texto através de um método WriteString.
fonte
TextWriter
precisa conversar com os doisBoldDecorator
eItalicDecorator
(bidirecionalmente) para concluir o trabalho.Eu me inclinaria para a segunda solução. Parece mais elegante e flexível. Embora seja difícil imaginar outros tipos além de negrito, itálico e sublinhado (sublinhado?), Seria difícil adicionar novos tipos usando variáveis de membro.
Eu usei essa abordagem em um dos meus projetos mais recentes. Eu tenho uma classe que pode ter vários atributos booleanos. O número e os nomes dos atributos podem mudar com o tempo. Eu os guardo em um dicionário. Se um atributo não estiver presente, presumo que seu valor seja "false". Também tenho que armazenar uma lista de nomes de atributos disponíveis, mas isso é outra história.
fonte
Eu acho que você está pensando demais sobre o assunto.
styles.setBold(true)
estyles.setItalic(false)
estão bemfonte