Estou programando em linguagens procedurais há algum tempo, e minha primeira reação a um problema é começar a dividi-lo em tarefas a serem executadas, em vez de considerar as diferentes entidades (objetos) que existem e seus relacionamentos.
Eu tive um curso universitário em OOP e entendo os fundamentos de encapsulamento, abstração de dados, polimorfismo, modularidade e herança.
Eu li /programming/2688910/learning-to-think-in-the-object-oriented-way e /programming/1157847/learning-object-oriented-thinking , e analisarei alguns dos livros apontados nessas respostas.
Penso que vários dos meus projetos de médio e grande porte se beneficiarão do uso efetivo do OOP, mas como iniciante, gostaria de evitar erros comuns e demorados.
Com base em suas experiências, quais são essas armadilhas e quais são as formas razoáveis de contorná-las? Se você pudesse explicar por que elas são armadilhas, e como sua sugestão é eficaz para resolver o problema, ela será apreciada.
Estou pensando na linha de algo como "É comum ter um número razoável de métodos de observação e modificador e usar variáveis privadas ou existem técnicas para consolidá-las / reduzi-las?"
Não estou preocupado em usar o C ++ como uma linguagem OO pura, se houver boas razões para misturar métodos. (Remanescente dos motivos para usar GOTOs, embora com moderação.)
Obrigado!
fonte
Respostas:
Uma coisa importante que aprendi é projetar classes de fora para dentro. Projete a interface antes mesmo de começar a pensar na implementação. Isso tornará a classe muito, muito mais intuitiva para seus usuários (aqueles que a utilizam) do que escrever os algoritmos subjacentes e criar a classe, além de escrever novas funções públicas de membros conforme necessário.
fonte
Bem, a primeira é a armadilha de expor muita informação. O padrão deve ser
private
, nãopublic
.Depois disso vem muitos getters / setters. Digamos que eu tenho um membro de dados. Eu realmente preciso desses dados em outra classe? Ok, faça um getter. Eu realmente, reaally necessário alterar esses dados durante a vida útil do objeto? Então faça um levantador.
A maioria dos programadores iniciantes tem o padrão de criar um getter / setter para cada membro de dados. Isso desorganiza as interfaces e geralmente são más escolhas de design.
fonte
Quando cruzei esse abismo, decidi pela seguinte abordagem:
0) Comecei devagar, com aplicativos procedimentais de tamanho pequeno / médio e nenhum item de missão crítica em ação.
1) um mapeamento simples da 1ª passagem de como eu escreveria o programa do zero no estilo OO - o mais importante para mim naquele momento, e isso é subjetivo - era descobrir todas as classes base. Meu objetivo era encapsular o máximo possível nas classes base. Métodos virtuais puros para todo o possível nas classes base.
2) O próximo passo foi criar as derivações.
3) a etapa final foi - no código processual original, separar as estruturas de dados do código obsever / modificador. Em seguida, use a ocultação de dados e mapeie todos os dados comuns nas classes base, e nas subclasses foram os dados que não eram comuns em todo o programa. E o mesmo tratamento para o código processador de observador / modificador - toda a lógica 'usada em todos os lugares' foi inserida nas classes base. E visualize / modifique a lógica que apenas atuou em um subconjunto dos dados foi para as classes derivadas.
Isso é subjetivo, mas é RÁPIDO, se você conhece bem o código processual e as estruturas de dados. Uma FALHA em uma revisão de código ocorre quando um pouco de dados ou lógica não aparece nas classes base, mas é usado em qualquer lugar.
fonte
Dê uma olhada em outros projetos bem-sucedidos que usam OOP para ter uma noção de bom estilo. Eu recomendo olhar para o Qt , um projeto que sempre admiro ao tomar minhas próprias decisões de design.
fonte
Dependendo de quem você fala, todos os dados no OOP devem ser privados ou protegidos e estar disponíveis apenas através de acessadores e mutadores. Em geral, acho que essa é uma boa prática, mas há ocasiões em que eu divergo dessa norma. Por exemplo, se você tem uma classe (em Java, digamos) cujo único objetivo é agrupar alguns dados em uma unidade lógica, faz sentido deixar os campos públicos. Se for uma cápsula imutável, apenas marque-a como final e inicialize-a no construtor. Isso reduz a classe (neste caso) a pouco mais que uma estrutura (na verdade, usando C ++, você deve chamar isso de uma estrutura. Funciona como uma classe, mas a visibilidade padrão é pública e sua intenção é mais clara), mas Acho que você descobrirá que usá-lo é muito mais confortável nesses casos.
Uma coisa que você definitivamente não quer fazer é ter um campo que deve ser verificado quanto à consistência no mutador e deixá-lo público.
fonte
struct
s - isso não faz diferença semântica, mas esclarece a intenção e faz com que seu uso pareça mais natural, especialmente para programadores em C.O livro Implementation Patterns, de Kent Beck, é uma excelente base para o uso de mecanismos orientados a objetos, e não ao mau uso.
fonte
Não estou preocupado em usar o C ++ como uma linguagem OO pura, se houver boas razões para misturar métodos. (Remanescente dos motivos para usar GOTOs, embora com moderação.)
Eu realmente não achava que tinha muito a oferecer a conversa até ver isso. Eu tenho que discordar do sentimento. OOP é apenas um dos paradigmas que podem e devem ser usados em C ++. Francamente, na minha opinião, não é uma de suas características mais fortes.
Do ponto de vista do OO, acho que o C ++ fica um pouco aquém. A ideia de ter funções não virtuais, por exemplo, é um sinal contra isso a esse respeito. Eu tive argumentos com aqueles que discordam de mim, mas membros não virtuais simplesmente não se encaixam no paradigma no que me diz respeito. Polimorfismo é um componente chave para OO e classes com funções não virtuais não são polimórficas no sentido OO. Então, como uma linguagem OO, acho que o C ++ é realmente bastante fraco quando comparado a linguagens como Java ou Objective-C.
Programação genérica, por outro lado, o C ++ possui essa muito boa. Ouvi dizer que também existem linguagens melhores para isso, mas a combinação de objetos e funções genéricas é algo bastante poderoso e expressivo. Além disso, pode ser extremamente rápido, tanto no tempo de programação quanto no tempo de processamento. É realmente nessa área que eu acho que o C ++ brilha, embora seja certo que poderia ser melhor (suporte à linguagem de conceitos, por exemplo). Alguém que pensa que deveria seguir o paradigma OO e tratar os outros na ordem da declaração goto em níveis de imoralidade está realmente perdendo por não olhar para esse paradigma.
A capacidade de metaprogramação de modelos também é bastante impressionante. Confira a biblioteca Boost.Units, por exemplo. Esta biblioteca fornece suporte de tipo para quantidades dimensionais. Fiz uso extensivo dessa biblioteca na empresa de engenharia em que trabalho atualmente. Ele apenas fornece um feedback muito mais imediato para um aspecto do possível programador ou mesmo erro de especificação. É impossível compilar um programa que use uma fórmula em que os dois lados do operador '=' não sejam dimensionalmente equivalentes sem a conversão explícita. Pessoalmente, não tenho experiência com nenhum outro idioma em que isso seja possível e, certamente, não com um que também tenha o poder e a velocidade do C ++.
A metaprogramação é um paradigma puramente funcional.
Realmente, acho que você já está adotando o C ++ com alguns conceitos errados infelizes. Os outros paradigmas além do OO não devem ser evitados, devem ser ALAVANCADOS. Use o paradigma natural para o aspecto do problema em que você está trabalhando. Não force objetos sobre o que não é essencialmente um problema propenso a objetos. Para mim, o OO não é nem metade da história do C ++.
fonte
Queria aceitar uma resposta para essa pergunta, mas não consegui decidir uma resposta para dar a marca de seleção. Como tal, votei os autores originais e criei isso como uma resposta resumida. Obrigado a todos que levaram alguns minutos, descobri que o insight que você forneceu me deu uma boa direção e um pouco de garantia de que eu não estava fora dos trilhos.
@nightcracker
Eu senti que tinha observado esse problema em ação no passado. Seus comentários também me fizeram lembrar que, ao ocultar as variáveis subjacentes e sua implementação, tenho a liberdade de alterar sua implementação sem destruir nada que dependa delas.
Dominic Gurto
Eu pensei que o comentário de Dominic era um ótimo ideal para aspirar, mas acho que o comentário de Gene realmente atinge a realidade da situação. Até agora, vi isso em ação ... e me sinto um pouco melhor por não ser incomum. Penso que, à medida que amadurecer como programador, vou me inclinar para projetos mais completos, mas agora ainda sofro com o salto e recebo algum código por escrito.
wantTheBest
Isso faz muito sentido ... Gostei da ideia de manter as coisas funcionando, mas refatorar algumas das coisas não críticas com as classes.
jpm
Eu sei há algum tempo que esse é um dos pontos fortes de encapsular os dados ... poder impor consistência e, por esse motivo, condições / intervalos / etc.
Crazy Eddie
Originalmente, eu errei muito na resposta de Crazy Eddie, acho que porque não tinha lido alguns dos tópicos mencionados ... como metaprogramação. Eu acho que a grande mensagem no post da CE foi que o C ++ é uma mistura de recursos e estilos que cada um deve ser usado com seu melhor potencial ... incluindo o imperativo, se é o que faz sentido.
Então, novamente, obrigado a todos que responderam!
fonte
A maior armadilha é a crença de que OOP é uma bala de prata ou o "único paradigma perfeito".
fonte