Você pode implementar programação "orientada a objetos" sem a palavra-chave class?

29

Digamos que desejamos fornecer uma abstração de uma "conta" em um banco. Aqui está uma abordagem, usando um functionobjeto em Python:

def account():
    """Return a dispatch dictionary representing a bank account.

    >>> a = account()
    >>> a['deposit'](100)
    100
    >>> a['withdraw'](90)
    10
    >>> a['withdraw'](90)
    'Insufficient funds'
    >>> a['balance']
    10
    """
    def withdraw(amount):
        if amount > dispatch['balance']:
            return 'Insufficient funds'
        dispatch['balance'] -= amount
        return dispatch['balance']
    def deposit(amount):
        dispatch['balance'] += amount
        return dispatch['balance']
    dispatch = {'balance': 0,
                'withdraw': withdraw,
                'deposit': deposit}
    return dispatch

Aqui está outra abordagem usando a abstração de tipo (por exemplo, classpalavra-chave em Python):

class Account(object):
    """A bank account has a balance and an account holder.

    >>> a = Account('John')
    >>> a.deposit(100)
    100
    >>> a.withdraw(90)
    10
    >>> a.withdraw(90)
    'Insufficient funds'
    >>> a.balance
    10
    """



    def __init__(self, account_holder):
        self.balance = 0
        self.holder = account_holder

    def deposit(self, amount):
        """Add amount to balance."""
        self.balance = self.balance + amount
        return self.balance

    def withdraw(self, amount):
        """Subtract amount from balance if funds are available."""
        if amount > self.balance:
            return 'Insufficient funds'
        self.balance = self.balance - amount
        return self.balance

Meu professor iniciou o tópico "Programação orientada a objetos" introduzindo a classpalavra - chave e mostrando-nos os seguintes pontos:

Programação orientada a objetos

Um método para organizar programas modulares:

  • Barreiras de abstração
  • Passagem de mensagem
  • Agrupando informações e comportamento relacionado

Você acha que a primeira abordagem seria suficiente para satisfazer a definição acima? Se sim, por que precisamos da classpalavra-chave para fazer programação orientada a objetos?

overexchange
fonte
2
Que bom que você concorda. =) Embora eu não conheça Python o suficiente para dar uma resposta completa, você pode estar interessado em saber que, em Javascript, a maneira típica de fazer OOP é semelhante ao "objeto de função" que você descreve (embora também tenhamos uma herança prototípica que permite que os objetos "compartilhem" métodos em vez de ter cópias separadas de cada método em cada objeto; presumo que o Python classfaça uma otimização semelhante).
Ixrec
Se você quiser uma resposta detalhada, faça outra pergunta ou entre na sala de bate-papo, mas a resposta curta é (se você ignora completamente a herança prototípica, matrizes etc.), isso é basicamente verdade; a maioria dos objetos JS nada mais são do que dicionários de chaves de cadeia para valores arbitrários. foo.bar()é geralmente idêntico e foo['bar'](), em raras ocasiões, a última sintaxe é realmente útil.
Ixrec
8
Esta é uma pergunta realmente importante no seu caminho para uma compreensão fundamental do POO. Se você estiver interessado, pode ler um post no meu blog onde eu crio um sistema de objetos simples em JavaScript sem depender de nenhuma das partes OOP da linguagem. Seu primeiro exemplo tem uma falha importante: onde você escreveria object['method'](args), os objetos Python realmente fazem o equivalente a object['method'](object, args). Isso se torna relevante quando uma classe base chama métodos em uma classe filho, por exemplo, no Padrão de Estratégia.
amon
13
Como outros observaram, esta é uma pergunta perceptiva sobre POO. Aproveitarei esta oportunidade, no entanto, para observar que isso não é de modo algum como os bancos reais representam as contas bancárias. Os bancos não têm um objeto "conta" mutável que muda quando você o debita e credita; eles têm uma lista de transações somente gravação e, em seguida, calculam o saldo a partir da lista de transações. Como um bom exercício, tente implementar esse mecanismo em vários idiomas.
Eric Lippert

Respostas:

66

Parabéns! Você redescobriu o fato conhecido de que a orientação a objetos pode ser feita sem suporte específico à linguagem de programação. É basicamente da mesma maneira que os objetos são introduzidos no esquema neste livro de texto clássico . Observe que Scheme não possui uma classpalavra - chave ou algum tipo de equivalente, e os objetos podem ser criados sem ter mesmo classes.

No entanto, o paradigma orientado a objetos foi tão bem-sucedido que muitas linguagens - e Python não é exceção - fornecem suporte interno para ele. Isso é simplesmente para facilitar o uso do paradigma pelos desenvolvedores e fornecer uma forma padrão de orientação a objetos para esse idioma. É essencialmente a mesma razão pela qual muitas linguagens fornecem um forloop, embora ele possa ser emulado usando um whileloop com apenas uma ou duas linhas de código adicionais - simplesmente facilidade de uso .

Doc Brown
fonte
"para fornecer uma forma padrão de orientação a objetos para esse idioma" Eu recebo críticas do JavaScript? ;)
jpmc26 25/05
1
@ jpmc26: não intencionalmente. E parece que existem alguns padrões amplamente aceitos de como os objetos são criados em JavaScript.
Doc Brown
@overexchange: você tem uma pergunta a fazer?
Doc Brown
1
@overexchange: Bem, o que significa OOP é discutível, existem diferentes escolas de pensamento, mas a definição do SICP é praticamente a dos 3 pontos principais da sua pergunta. Definitivamente, trata-se de construir abstrações, mas não se esqueça dos pontos 2 e 3. Sim, o conceito OOP inclui "mudança de estado", mas também permite o conceito de "objetos imutáveis" (como a classe de string em Java ou C #, Python possui alguns tipos de dados mutáveis ​​e imutáveis). E seu primeiro exemplo em sua pergunta confirma essa definição, bem como seu segundo exemplo.
Doc Brown
2
@overexchange: isso remonta à definição de orientação a objetos de Alain Kay (o inventor da linguagem da conversa fiada). Você encontrará uma resposta abrangente neste artigo anterior do SO stackoverflow.com/questions/2347973/… . IMHO "mensagem que passa entre objetos" no sentido SICP significa apenas não acessar os dados internos de um objeto diretamente, apenas através de um "protocolo de comunicação definido". Em linguagens OO como Python, isso pode significar apenas "chamar o método de um objeto".
Doc Brown
13

Concordo que a primeira definição satisfaz os três pontos que seu professor fez. Acho que não precisamos da palavra-chave class para nada. Nos bastidores, o que mais é um objeto além de uma estrutura de dados com diferentes tipos de dados e funções para trabalhar com os dados? Obviamente, as funções também são dados.

Eu diria ainda mais que a programação orientada a objetos não depende muito das palavras - chave fornecidas pela sua linguagem; você pode fazer a programação orientada a objetos em C, se assim o desejar! De fato, o kernel do linux emprega essas técnicas.

O que você pode deduzir da palavra-chave class aqui é que a linguagem fornece suporte para esse tipo de construção pronta para uso, e você não precisa realizar todos os processos para reimplementar a funcionalidade sozinho (o que é uma tarefa bastante divertida em em si!). Sem mencionar todo o açúcar sintático que você pode obter também.

Zavior
fonte
e a herança? Somos cruciais sobre subtipos / supertipos em tempo real? Minha primeira abordagem pode não divertir isso !!
Overexchange
5
A herança não é de forma alguma necessária para o POO. Você também pode implementar herança no seu primeiro exemplo. Pode não ser muito "limpo", mas possível da mesma forma.
Zavior 24/05
3
@ Zavior esse comentário me faz pensar em VB6. O objeto orientado sem herança realmente cria um código menos limpo, para dizer o mínimo.
RubberDuck
1
@overexchange Quando você pensa sobre isso, herança é tudo sobre compartilhamento de código / comportamento comum entre classes. Nada impede você de repetir todo esse código o tempo todo. Seria super horrível de manter. Há uma razão pela qual existe herança :)
Zavior
1
@Zavior Na sua forma mais básica, "subclasse" é uma abstração que diz "antes de você retornar a função de despacho e dados com ordem superior que estou definindo aqui (que fingiremos ser uma" classe "ha ha ha), instancia a função de despacho e dados com 'superclasse' referida por ThisParentFoo ". Isso é realmente tudo o que é. Quando se trata de herança múltipla ingênua, isso ainda é tudo, mas com a ressalva de que você introduz o "problema do diamante", e é por isso que a herança múltipla é péssima.
Zxq9
9

Claro que você pode!

A linguagem de programação automática é uma linguagem dinâmica orientada a objetos, baseada em protótipo, na qual tudo é um objeto e não há senso de classe ou qualquer outra coisa. Ele se concentra na idéia de objetos prototípicos e na ideia de cloná-los, em vez de ter classes como modelos de como criar objetos.

Você deve verificar http://www.selflanguage.org/ para obter mais informações. Eu acho que é muito interessante e se você gosta de OOP, é uma boa ideia verificar algo que não é tão comum.

DraQ
fonte
0

Nem sempre: depende do idioma. Você demonstrou a capacidade de fazer isso no Python, mas (se sua pergunta é agnóstica à linguagem, apesar da tag Python), nem todas as linguagens podem fazer isso. Java, por exemplo, geralmente não pode. Ignorando a classe que contém main, não há como definir métodos / campos arbitrários em um objeto definido no main sem a palavra-chave class. Embora existam classes anônimas, elas exigem uma interface e não podem ter nenhum membro público, exceto aqueles definidos na interface. Embora seja possível definir interfaces personalizadas e criar classes anônimas para elas, isso é efetivamente o mesmo (mas menos conveniente) do que simplesmente usar uma classe.

Doc Brown tem uma ótima resposta, mas o que estou tentando dizer é que tenho certeza de que há pelo menos um idioma que não permitirá a sua solução.

SkySpiral7
fonte
Como iniciante, para aprender o conceito de "Programação Orientada a Objetos", sim, estou tentando ser independente da linguagem. Eu acho que "Doc Brown" deu resposta nas mesmas linhas, ele me disse para ler sicp text-chap3, que não tem nada a ver com nenhuma sintaxe de linguagem.
overexchange
Gostaria de nomear um idioma que exija absolutamente o uso de classes para validar minha resposta. Mas eu sei apenas algumas linguagens e, infelizmente, o Java permite uma solução alternativa. O C ++ possui estruturas e o Javascript permite o que você demonstrou. Suspeito que Smalltalk e Eiffel possam exigir aulas, pois soube que eles são estritamente estruturados.
SkySpiral7
Como Doc Brown, se eu tivesse aprendido a usar o esquema, não teria feito essa pergunta. Infelizmente, a versão do curso SICP que estou aprendendo usa python.
overexchange
1
Todo programa Java válido deve conter a classpalavra - chave, para que não seja uma surpresa. Mas você absolutamente pode implementar seu próprio sistema de objetos em cima do sistema de objetos de Java, embora eu não saiba por que você deseja fazer uma coisa dessas.
Brian Gordon
1. O Java é realmente especial nesse sentido, pois simplesmente jogou fora todas as outras palavras-chave que podem ser usadas para criar estruturas de dados personalizadas. Quase todas as outras línguas que conheço têm registros ou fechamentos. 2. Mesmo em java, você pode programar em uma memória construída a partir de uma matriz. E você pode implementar a orientação a objetos dentro disso, usando a classpalavra - chave apenas porque a linguagem exige que você coloque suas funções nas classes. Obviamente, isso é extremamente teórico, mas mesmo em Java você pode fazer Orientação a Objetos sem as classes internas!
Cmaster
0

A definição do professor perde completamente o ponto mais importante da programação orientada a objetos, a única coisa que a torna útil e única. "Passagem de mensagens" é um monte de bobagens sonhadas pelo pessoal do Smalltalk, e tem sido um fracasso em todos os lugares em que foram tentadas. O verdadeiro poder do OOP é algo conhecido como substituição de Liskov , e embora o conceito seja bastante simples de descrever e entender, a implementação subjacente é complexa o suficiente para que seja essencialmente impossível fazer o certo sem o suporte no nível da linguagem.

A idéia da substituição de Liskov é que, em qualquer lugar em que seu código esteja esperando uma variável de um determinado tipo, ele poderá aceitar qualquer tipo derivado desse tipo e ainda funcionar corretamente sem precisar ter conhecimento dos detalhes do tipo derivado.

Por exemplo, as estruturas da GUI usam a substituição Liskov em todo o lugar. Eles tendem a ter uma Controlclasse base que pode representar "qualquer controle", que define uma interface que conhece ações básicas como desenhar, redimensionar e responder à entrada do usuário. Se você clicar em um controle, a estrutura da interface do usuário chamará um Clickmétodo no controle sem precisar se preocupar com o tipo de controle e, em seguida, deixar o controle manipular o clique da maneira apropriada para sua própria classe. Um Buttoncontrole deve fazer algo completamente diferente quando clicado em um TextBoxcontrole, para dar apenas um exemplo.

Portanto, sim, você pode criar algo parecido com objetos usando o truque de funções aninhadas descrito acima, mas como você não pode obter herança e substituição de Liskov dessa maneira, é um substituto extremamente limitado para o verdadeiro OOP.

Mason Wheeler
fonte
Na linguagem C, não posso dizer 'struct parent {}' e depois 'struct child {struct parent * ptr;}'? Isso não é herança na sintaxe da linguagem non-oop?
overexchange
@overexchange: Essa é uma tentativa não-OO de falsificá-lo, mas o compilador não permitirá que você substitua um pelo outro. (Você não pode passar a child*para uma função que usa a parent*como argumento, pelo menos não sem um typecast.) E ainda pior, as estruturas C não podem ter métodos vinculados a elas e não há suporte para métodos virtuais , que são o que faz a mágica da substituição de Liskov funcionar, então você precisa construir VMTs manualmente, o que é um processo complicado e fácil de estragar.
Mason Wheeler
1
O kernel do Linux usa uma emulação de várias técnicas de OO, que precisam ser codificadas manualmente, sem suporte a idiomas. Isso leva a muitas oportunidades de bugs, que, sendo Linux, são contrabalançados por uma aplicação liberal da Lei de Linus. Sim, é possível fazer isso - a equivalência de Turing prova isso -, mas meu argumento é extremamente difícil de acertar sem o suporte ao idioma ainda estar de pé. Além disso, por que todas essas perguntas sobre C quando a pergunta era sobre Python? Em C, não é possível executar o truque de funções aninhadas em primeiro lugar.
Mason Wheeler
1
@overexchange Desde quando o Java é um "paraíso dos programadores"?
Brandin
1
A passagem de mensagens não foi uma falha nos sistemas OOP Smalltalk, Erlang ou mesmo no estilo Java, em que "mensagem" significa algo diferente de "chamada de função" (sinais e slots do Qt com uma fila segura de thread VS marketing Java antigo usando o termo "mensagem" quando significa "chamada de método"). Mensagens! = Chamadas de função. As mensagens genuínas não são apenas bem-sucedidas, parecem ser a única maneira que sabemos de criar sistemas maciços e simultâneos e robustos. Isso é ortogonal à implementação de OOP no estilo Java sem a palavra-chave 'class'. Isso pode ser feito. É não sempre útil. Mensagens estão fora de questão.
Zxq9 23/10/2015
-1

Resposta rápida e curta

Sim, os programadores podem aplicar a programação orientada a objetos sem "classes".

Resposta descritiva extensa e aborrecida longa

Existem várias variações de "Orientação a Objetos", apesar de tudo, o primeiro conceito que vem à mente de muitos programadores é "Classes".

Sim, os programadores podem aplicar a programação orientada a objetos sem "classes", mas estão limitados aos recursos e limitações de cada linguagem de programação.

Sua postagem está marcada como Python ; portanto, o título da sua pergunta pode ser mais como "Como implementar a programação orientada a objetos sem classes no Python".

Atualmente, uso a frase "Programação Orientada a Objetos e Classes" para identificar outras variações, como "Prototipagem" do Javascript ou "Baseada em Visual Basic" ou emulação em "Pure C" usando "functors".

umlcat
fonte