From Procedural to OO

Eu e meu colega fizemos uma aplicação simples com essas funcionalidades:

  1. Solicitar chamado técnico/ serviço (assunto selecionado de uma lista)
  2. Consultar um chamado aberto por um atendente

Fizemos num estilo procedural como o Shoes fala, e como o Fernando Lozano me induziu:

Classes de dados:

  • ServiceRequest (funcionalidade 1) - representa os campos da requisição de serviço
  • Service (funcionalidade 2) - campos de um serviço já aberto
  • HistoryItem (funcionalidade 2) - cada serviço possui um histórico com itens que indicam o andamento do serviço

Classes de lógica

  • DateConverter (converte datas Java para outro formato usado no banco - não é o tipo “data” nativo do banco, é um float)
  • DataAccess com só três métodos:
public Service getService(int code, int password) throws DataAccessException...
public void saveServiceRequest(ServiceRequest serviceRequest) throws DataAccessException...
public List getAllSubjects() throws DataAccessException...

Ou seja:

  1. Obter um serviço (func. 2 - consulta)
  2. Salvar uma requisição de serviço (func. 1 - solicitação)
  3. Pegar a lista de assuntos possíveis numa solicitação (func. 1 - o assunto da solicitação é escolhido de uma lista)

Não estou deprimido por causa disso (o sistema é muito pequeno), mas depois de discutir sobre o assunto, só por curiosidade

ou exercício dos conceitos, fico pensando o seguinte:

Se eu for “oozar” o projeto puxa onde eu colocaria essses métodos, como mesclaria com as classes de dados??

Minha idéia no momento é a seguinte:

método 1) Quem provê um serviço? A empresa, os técnicos, o sistema… Mas pra que criar uma classe ServiceProvider, Technician, System, HelpDesk etc. só pra colocar esse método?? Os serviços simplesmente existem e têm uma etiquetinha de identificação. O usuário, de posse do número dessa etiqueta (como ele o obtém não é escopo do nosso sistema), simplesmente vai no meio dos objetos e “pega” o Service de seu interesse. Conclusão: este método eu moveria para a classe
service, como um metodo estático:

método 2) “Salvar” refere-se à persistência, que não é lógica de negócios, é um mecanismo de auxílio. Ou seja, quando o usuário quer fazer uma nova solicitação de serviço, ele simplesmente a faz, ele a cria. Conclusão: este método vai para um DAO (ignorem a persistência), assim, na lógica de negócios, uma nova solicitação se faria assim:

ServiceRequest serviceRequest = new ServiceRequest();
// preenche os dados do serviceRequest 
// Aqui acontece uma mágica, o objeto é salvo pro disco automaticamente (hoje em dia não é verdade)

método 3) Quem provê um assunto? Imagino ser parecido com o método 1, por isso os assuntos são limitados, mas existem por si só. Logo o usuário, durante uma requisição pegaria um assunto. O problema é que não queria criar uma classe Subject (e de fato não há) por que um assunto nada mais é que uma String, e um conjunto de assuntos, um List. Pensando que um assunto é uma característica de um ServiceRequest (assunto da solicitação de serviço), eu colocaria esse método na classe ServiceRequest como static:

List subjects = ServiceRequest.getAllPossibleSubjects();

No caso do DateConverter também não faz parte da lógica de negócios, mas de um DAO. Por isso seria movido para lá.

Ou seja, remodelando o sistema assim teríamos as seguintes classes:

  • Service
  • ServiceRequest
  • atualmente, um DAO (não é lógica de negócios)

Sugestões e opiniões?

Oi,

Se você consegue fazer os sitema com a mesma funcionaldiade com um console SQL apenas e só INSERT, DELETE, UPDATE e SELECT, realmente você pode não ter rum domínio que valha sequer ser modelado, mas eu duvido que seja tão simples assim.

Vou tratar o meu post pensando que sua aplicação não é tão simples assim (se for, porque usar Java em pimeiro lugar? Crie um script :wink: ).

Nesse caso específico, as regras de negócio deve estar espalhadas por outras partes do sistema, como interface.

Por exemplo, você pode ter uma regra na sua interface dizendo que o botão “alterar” só aparece para usuários com login de adminsitrador. Isso é uma regra de negócio (apenas administradores alteram chamados), e está implementada na interface, quando deveria estar no domínio. É esse tipo de informação que torna seu domínio rico, lembre-se: domínio é onde os problemas são processados :wink:

Reúna essas regras e tente visualizar quais delas pertencem à quais conceitos no seu domínio.

Shoes

Shoes, e como fazer essa lógica de negócio (atribuição de direitos) se integrar com a interface sem fazer merda?

Primeiro, pense que é uma validação. É como um campo int recebendo uma String.

O que fazemos com validação? A rigor, o objeto tem que ser responsável para garantir que vai receber um int, não uma String de caracteres.

Mas… você não vai usar um javaScriptzinho para garantir que alguém não digita por engano o login no lugar da idade? Você está duplicando a lógica para a comodidade.

Sim, você pode colocar uma verificação no botão, mas evite ao máximo duplicar código e faça apenas checagens, não processamento.

Shoes

Shoes, num intendi, mas tá ok…

Outra coisa que estava pensando é que getService terá de fazer acesso a DAO. Ou seja, as interfaces DAO farão parte da minha lógica de negócios, em vez de ser algo paralelo. Isso é ruim? Por quê?

Que tal parar de pensar em DAOs e comecar a usar Active Records?

http://www.martinfowler.com/eaaCatalog/activeRecord.html

Eu entendi errado ou esse Active Record significa colocar o que o DAO faz no próprio objeto de negócio, tipo o próprio objeto cuida de sua persistência? Isso não piora o acoplamento com o banco? Como ficaria um getService(id, senha)?

O problema é quando se tem diversas formas de persistencia.
Em um tópico discutimos isso…

Não, não é problema. Basta usar interfaces e injeção de dependências.

Eu to com esses links pra ler, talvez ajude em algo:

:arrow: http://www.guj.com.br/posts/list/0/20668.java
:arrow: http://www.guj.com.br/posts/list/0/21616.java
:arrow: http://www.guj.com.br/posts/list/0/21687.java

@PS: Ajude a mim :mrgreen:

A questão é separar a lógica de negócio e a implementação de persistencia.
ex:

interface Venda {
   public void registrar();
}

interface Estoque {
   public void darBaixa(int produto,int qtd);
}

class VendaImpl implements Venda {
   public void registrar() {
      // insert venda ...
      Estoque e = // pegar instancia
      e.darBaixa(1,3);
   }
}

class EstoqueImpl implements Estoque {
   public void darBaixa(int produto,int qtd) {
      // update estoque set qtdProd = qtdProd - qtd where produto = produto
   }
}

O problema é misturar o código de negócio com código de persistencia.

[quote=cv]Que tal parar de pensar em DAOs e comecar a usar Active Records?

http://www.martinfowler.com/eaaCatalog/activeRecord.html[/quote]
cv, pode dar um exemplo de como fazer isso em Java sem muita bronca? Outra coisa, se meus objetos de dominio possuem os dados, a logica de negocios e ainda resolvem a persistência, eles não estão um pouco sobrecarregados? Eu realmente estou muito interessado no Active Record, já que ele parece um bocado natural para mim, mas gostaria de ouvir opiniões sobre as implicações desse padrão.

LIPE, injetar as dependencias nos caras que no fim das contas farão a persistencia ou nos objetos de dominio?

valez…

Falei sobre o objeto que vai se encarregar da persistência.
interface Persistent
class PersistentXML implements Persistent
class PersistentHibernate implements Persistent
pronto.

E quanto ao ActiveRecord, é ótimo para objetos mais simples. São apenas 4 métodos a mais.

Não é melhor ter classes abstratas doque interfaces.
Assim vc reutiliza o código comum e pode separar as regras de negócio da persistencia.
ex:


abstract class Persistent {
  public final void metodoDeNegocio() {
     // valida e faz o que tem que fazer
     metodoDePersistencia();
     // pos-condicoes
  }

  public abstract void metodoDePersistencia();
}

class PersistentXML extends Persistent {
   public void metodoDePersistencia() {
       // faz persistencia
   }
} 

Persistent p = Persistent.obterInstancia();
p.metodoDeNegocio();

Nao, nao eh melhor usar classes abstratas do que interfaces, pq sao coisas diferentes e servem pra coisas diferentes, jprogrammer.

jack, o lance do Active Record, caso vc queira deixar os objetos mais limpinhos, eh delegar o maximo de funcionalidade possivel - especialmente a que trata do banco de dados - a classes “privadas” (ou seja, que nao fazem parte da especificacao publica da sua API). Exemplinho baba:

[code]
// Persistencia
public interface ActiveRecord {
public void saveOrUpdate(T object);
public void delete(T object);

// independencia de framework de persistencia eh burrice
public Session getSession();

}

// Regra de negocio qualquer
public interface LoginValidator {
public boolean validateLogin(String user, String passwd);
}

// Objetao publico - eh esse que todo o resto do sistema vai ver

public class User implements
ActiveRecord,
LoginValidator,
Foo,
Bar, {

// Servicos
private LoginValidator loginValidator;
private ActiveRecord activeRecord;
private Foo foo;
private Bar bar;

// Dados
private String userName;
private String password;
private String email;
private Date lastLogin;
private Date userSince;
private …

public User() {
// null objects caso alguem queira que isso so sirva de DTO
activeRecord = new NullActiveRecord();
loginValidator = new NullLoginValidator();
foo = new NullFoo();
bar = new NullBar();
}

public User(ActiveRecord activeRecord, LoginValidator loginValidator, Foo foo, Bar bar) {
this.activeRecord = activeRecord;
this.loginValidator = loginValidator;
this.foo = foo;
this.bar = bar;
}

public void saveOrUpdate() {
activeRecord.saveOrUpdate(this);
}

public void saveOrUpdate(User u) {
activeRecord.saveOrUpdate(u);
}

// ‘finders’ praticos e bonitos e que mantem o intestino funcionando regularmente

public static SortedSetfindByUserName(String userName) {
try {
SortedSet ret = activeRecord.getSession().find(…);
} catch(HibernateException e) {
throw new UserException(e);
}
}

}[/code]

Duh. Pensei melhor, aqui, e aquele construtor recebendo as implementacoes expoe as interfaces pra fora do sistema. Not good. Substituam por:

[code]
// Objetao publico - eh esse que todo o resto do sistema vai ver

public class User implements
Serializable, // esqueci desse cara tambem
ActiveRecord<User>,
LoginValidator,
Foo,
Bar<User>, {

public User() {
Configuration cfg = Configuration.getThreadLocalInstance(); // singleton NAO

loginValidator = cfg.getLoginValidator();
activeRecord = cfg.getActiveRecord();
foo = cfg.getFoo();
bar = cfg.getBar();

}

}[/code]

Achei interessante, mas fiquei com algumas dúvidas.

  • Porque a classe User usa o ActiveRecord internamente e implementa os seus métodos.
    Desse jeito vc não está expondo seus métodos de persistencia ?
    Acho legal somente expor os metodos de negocio.
  • Vc poderia instancia o ActiveRecord de fora da classe User
    Desse jeito poderia manipular “dados” sem passar pelos constraints e validações da regra de negócio ?

[quote=jprogrammer]- Porque a classe User usa o ActiveRecord internamente e implementa os seus métodos.
Desse jeito vc não está expondo seus métodos de persistencia ?[/quote]

Não! O User aqui é mais do que um VO ou DTO, ou seja, ele tem comportamento e um deles diz que ele sabe salvar seu estado.

Isso é chamado composição … no fim das contas o objeto nao sabe fazer isso, mas usa um objeto que sabe.

Alguém ai, certo?

O que tem de errado em expor os metodos de persistencia? Alguem tem que fazer isso, e nada mais natural do ponto de vista OO do que um User que sabe se salvar no banco (ou sabe falar com quem sabe salva-lo no banco).

Poder, pode; dever, nao deve. O resto do sistema - aquela parte chata que fica em volta do domain model - nao deveria nem saber que o ActiveRecord existe. Deveria falar com User, e soh com ele.

Antes que vc pergunte, nao, nao da pra forcar esse constraint em Java a menos que vc use alguma ferramenta externa. E tambem nao tem nada de mal em documentar isso bem e avisar aos outros desenvolvedores do projeto sobre como a coisa deve funcionar.