Mostrando postagens com marcador mvc. Mostrar todas as postagens
Mostrando postagens com marcador mvc. Mostrar todas as postagens

sexta-feira, setembro 26, 2008

Python e MVC - Parte 2

Continuando o post anterior Python e MVC - Parte 1.

Durante o desenvolvimento de uma aplicação aqui na empresa estive decidido a utilizar um framework MVC para Python que além de me prover as ferramentas necessárias para resolver os meus dilemas de design como manutenção facilitada, separação da aplicação em partes lógicas e criação de uma estrutura de componentes este também deveria ser genérico o suficiente para permitir a troca da view na minha aplicação de maneira simples e fácil.

O problema é que hoje existem basicamente três frameworks MVC para Python: Kiwi, pygtkmvc e PureMVC. O Kiwi é um framework muito bom e desenvolvido para uma aplicação real o Stoq, o pygtkmvc apesar de ter idéias interessantes parece ser um projeto abandonado e o PureMVC por sua vez é um projeto iniciado em ActionScript que mais tarde foi portado para diversas linguagens entre elas Python.

O Kiwi e o pygtkmvc são altamente acoplados com a view no caso PyGtk, o que vai totalmente contra o princípio de fraco acoplamento entre as partes como diz o MVC, e isso IMHO é muito importante e também é um requisito da minha aplicação. Sendo assim, sobra apenas nosso amigo PureMVC.

Eu também poderia construir meu próprio framework MVC, de acordo com minhas necessidades e me baseando nestes últimos três exemplos, reunindo assim o melhor de cada um. Sem dúvida, essa seria a melhor alternativa, mas como sempre o tempo era curto e essa não era uma possibilidade. Sem falar no esforço enorme de análise e um conhecimento muito profundo de design patterns necessário para se criar algo genérico o suficiente.

O PureMVC possui alguns problemas, IMHO. O primeiro problema é seu estilo ou filosofia e sua nomenclatura que são extremamente diferentes do que estamos acostumados em Python. A implementação então nem se fala, obviamente foi feita por um programador Java ou algo do tipo, embora funcione.

Algumas coisas não seguem uma linha de simplicidade e facilidade no uso, tornando coisas simples um pouco complexas demais. Como exemplo, o código abaixo foi retirado do demo do projeto que utiliza wxPython:


class ProductListMediator(Mediator, IMediator):
NAME = "ProductListMediator"
product_proxy = None
def __init__(self, view_component):
self.view_component = view_component
super(ProductListMediator, self).__init__(self.NAME, view_component)


O código acima pode ser simplicado com uma meta class como essa:


class MetaMediator(type):
def __init__(cls, name, bases, namespace):
cls.NAME = name


class BaseMediator(Mediator, IMediator):
__metaclass__ = MetaMediator
def __init__(self, view_component=None, constant_list=[]):
self.constant_list = constant_list
super(BaseMediator, self).__init__(self.NAME, view_component)



Obtendo o mesmo resultado que o código original sem perder em flexibilidade:


class ProductListMediator(BaseMediator):
product_proxy = None


O PureMVC reune uma gama de padrões em uma solução bem interessante. Todas as partes do MVC são agrupadas e invocadas por um padrão facade, este facade invoca o controller através do padrão command utilizando um esquema de execução por variáveis globais, com essa solução o controller não fica acoplado ao facade, já que se o nome do método for alterado, basta alterar em um local, no alias definido na variável global.


class AppFacade(Facade):
ON_ACT_STARTUP = 'on_act_startup'

@staticmethod
def getInstance():
return AppFacade()

def initializeController(self):
super(AppFacade, self).initializeController()
super(AppFacade, self).registerCommand(AppFacade.ON_ACT_STARTUP,\
controller.StartupCommand)


O controller por sua vez utiliza o padrão observer para se comunicar com a view garantido fraca acoplamento com o uso de notificações.


class DeleteCommand(SimpleCommand, ICommand):
def execute(self, note):
product = note.getBody()
product_proxy = self.facade.retrieveProxy(model.ProductProxy.NAME)
product_proxy.delete(product)
self.facade.sendNotification(main.AppFacade.ON_PRODUCT_DELETE)


A view então utilizando o padrão mediator para se comunicar com a visão concreta Gtk, Html, etc mantem mais uma vez acoplamento fraco, se for preciso mudar de Gtk para Html ou Qt, muito pouco do código precisará ser modificado, sanando a minha necessidade inicial.


class DialogMediator(Mediator, IMediator):
NAME = "DialogMediator"
def __init__(self, view_component):
self.view_component = view_component

def listNoticationInterests(self):
return [main.AppFacade.ON_SHOW_DIALOG,]


E por fim o model utiliza o padrão proxy para garantir abstração por interface, assim proporcionando independência da tecnologia utilizada no model, ORM, XML, serialização, etc.


class ProductProxy(Proxy):
NAME = "ProductProxy"
def __init__(self):
super(ProductProxy, self).__init__(self.NAME, [])

def get_all(self):
return value_object.ProductVO.query.all()


O esquema utilizado pelo PureMVC é um tanto complexo a primeira vista, mas se for utilizado com bom senso e sempre com simplicidade em mente o retorno pode ser muito positivo. Além da fraco acoplamento a manutenção fica muito fácil, já que cada entidade da aplicação fica bem separada evitando propagação de erros pela aplicação. Vale lembrar que o PureMVC funciona como um esqueleto para sua aplicação não obrigando o uso explicito de todos os componentes.

Como o PureMVC é feito para um conjunto de linguagens ao qual o Python não se encaixa, uma camada acima do PureMVC se faz necessária afim de simplificar a utilização deste e excluindo as partes desnecessárias, como os exemplos mostrados acima.

Uma dica é simplificar o esquema de notificações do mediator para que as notificações executem os métodos da classe implementa o mediator que tiverem o mesmo nome das notificações, excluindo assim a necessidade do registro e criação das notificações na classe. Como exemplo abaixo, retirado da mesma demo:


def listNotificationInterests(self):
return [
main.AppFacade.ON_PRODUCT_CHANGE,
main.AppFacade.ON_PRODUCT_DELETE,
]

def handleNotification(self, note):
if note.getName() in [main.AppFacade.ON_PRODUCT_CHANGE, main.AppFacade.ON_PRODUCT_DELETE]:
self.view_component.clear()
self.view_component.fill(self.product_proxy.get_all())


Neste parte de código o programador precisa especificar quais as notificações a classe recebe e escrever um método com um monte de condicionais para tratar cada notificação. É muito mas fácil escrever uma metaclass para transformar isso em um esquema de sinal que invoca o sinal que tiver um método na classe com o mesmo nome do sinal.

Fica na minha lista de coisas a fazer uma versão deste framework para Python, com um código e uma filosofia adaptada ao estilo Python de ser.

Por fim um link interessante sobre o PureMVC.

quinta-feira, setembro 18, 2008

Python e MVC - Parte 1

É fato que durante o desenvolvimento de uma aplicação mais complexa alguns problemas ou dilemas
podem fazer nosso cérebro fritar. Para que o programador alcance uma boa solução é recomendável
que ele se concentre no contexto do problema e da solução. Python alcança esse objetivo de maneira magnífica, com um alto nível de abstração, simplicidade e coesão a linguagem permite que o programador se desvincule da linguagem e fique livre para se focar na solução.

No entanto, alguns problemas não fazem parte do contexto do problema e da solução, estes geralmente são ocasionados por nós mesmos, seja por falta de conhecimento da linguagem em questão, de organização, planejamento ou ainda pelo uso de uma solução pouco elegante ou inadequada.

Um sistema que se inicia de maneira desorganizada e sem planejamento com o passar do tempo e com evolução natural do mesmo, tende a enfrentar dificuldades assustadoras em relação a manutenção.

Qual a solução? Simples, vamos nos organizar, planejando e definindo muito bem como nosso sistema deve ser feito, vamos utilizar um bom processo de analise e pronto! Esse já é um grande passo, mas além desses pontos precisamos nos perguntar, será que os problema que estou tentando resolver já não possuem uma solução?

Sim, alguns problemas já possuem soluções "amplamente" divulgadas e aceitas, essas soluções são chamadas de Design patterns (Padrões de projeto) ou Architectural patterns (Padrões de arquitetura de software).

O MVC (model, view, controller) é um padrão de arquitetura que serve para separar conceitos lógicos da sua aplicação. O grande problema é que o próprio MVC, como todo padrão, por si só, não resolve todos os problemas e algumas vezes pode acabar criando outros problemas. O uso ideal de MVC com alguns cuidados e uma série de padrões de projeto pode nos garantir uma aplicação muito bem escrita e de fácil manutenção.

Inicialmente utilizar MVC e outros padrões fielmente pode ser um tanto chato e cansativo, mas como o tempo esse esforço pode render bons frutos. Aliar esses padrões com a simplicidade e elegância do Python não é um equilíbrio tão fácil de se alcançar, mas que pode ser muito gratificante.

Quando utilizo algum padrão em Python sempre procuro ter em mente que este padrão pode não se aplicar em Python, ou seja, tento não aceitar esses padrões como únicas e infalíveis soluções.

No próximo post vou falar sobre os frameworks MVC para Python (Kiwi, PureMVC, etc), quais as opções e detalhes de implementação.