programação orientada a aspectos com c#
DESCRIPTION
Programação orientada a aspectos com C#. Paulo Borba e André Furtado Centro de Informática Universidade Federal de Pernambuco. Programação orientada a aspectos é. uma nova técnica de programação que oferece suporte à modularização de crosscutting concerns. - PowerPoint PPT PresentationTRANSCRIPT
Programação orientada a aspectos com C#
Paulo Borba e André FurtadoCentro de Informática
Universidade Federal de Pernambuco
Programação orientada a aspectos é...
uma nova técnica de programação
que oferece suporte à
modularização de crosscutting concerns
Ou seja, programação orientada a aspectos é...
uma nova técnica de programação
que oferece suporte à
modularização de “requisitos” que afetam várias
partes de uma aplicação
Persistência é um crosscutting concern
public class Conta { private string numero; private double saldo; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) { if (valor > saldo) { throw new SaldoInsuficienteException)); } else { this.saldo = this.saldo - valor; } } public void Atualizar(Conta c) { this.numero = c.numero; this.saldo = c.saldo; } }
public class RepositorioContasAccess : RepositorioContas { public void Inserir(Conta conta) { string sql = "INSERT INTO Conta (NUMERO,SALDO) VALUES ('" + conta.Numero + "'," + conta.Saldo + ")"; OleDbCommand insertCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); insertCommand.ExecuteNonQuery(); } public void Atualizar(Conta conta) { string sql = "UPDATE Conta SET SALDO = (" + conta.Asldo + ") WHERE NUMERO = '" + conta.Numero + "'"; OleDbCommand updateCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); int linhasAfetadas; linhasAfetadas = updateCommand.ExecuteNonQuery(); if (linhasAfetadas == 0) { throw new ContaNaoEncontradaException(conta.Numero); } } }
public class CadastroContas { private RepositorioContas contas; public void Debitar(string numero, double valor) { Conta c = contas.Procurar(numero); c.Debitar(valor); contas.Atualizar(c); } public void Transferir(string numeroDe, string numeroPara, double valor) { Conta de = contas.Procurar(numeroDe); Conta para = contas.Procurar(numeroPara); de.Debitar(valor); para.Creditar(valor); contas.Atualizar(de); contas.Atualizar(para); } public double Saldo(string numero) { Conta c = contas.Procurar(numero); return c.Saldo; } }}
public class Banco { private CadastroContas contas; private Banco() { contas = new CadastroConta (new RepositorioContasAccess()); } public void Cadastrar(Conta conta) { Persistence.DBHandler.StartTransaction(); try { contas.Cadastrar(conta); Persistence.DBHandler.CommitTransaction(); } catch (System.Exception ex){ Persistence.DBHandler.RollBackTransaction(); throw ex; } } public void Transferir(string numeroDe, string numeroPara, double valor) { Persistence.DBHandler.StartTransaction(); try { contas.Transferir(numeroDe, numeroPara, valor); Persistence.DBHandler.CommitTransaction(); } catch (System.Exception ex){ Persistence.DBHandler.RollBackTransaction(); throw ex; } } }
public class DBHandler { private static OleDbConnection connection; public static OleDbConnection Connection { get { if (connection == null) { string dataSource = "BancoCS.mdb"; string strConexao = "Provider= Microsoft.Jet.OLEDB.4.0; " + "Data Source=" + dataSource; connection = new OleDbConnection(strConexao); } return connection; } } public static OleDbConnection GetOpenConnection() { Connection.Open(); return Connection; }
public static void StartTransaction() { Connection.Open(); transaction = Connection.BeginTransaction(); }
}
Código de persistência em vermelho…
Crosscutting concerns…
Afetam várias partes da aplicação São relativos à decomposição
dominante• via classes no caso de OO• via funcões no caso das linguagens
funcionais
Exemplos de crosscutting concerns Distribuição Controle de concorrência Tratamento de exceções Logging Debugging Variações em linhas de produtos de
software Suporte a eventos
Há várias técnicas para modularizacao Procedimentos Classes, herança e subtipos Padrões (arquitetura em camadas)
Aspectos
Foco em modularizar crosscutting concerns
As técnicas de modularização...São complementares e ajudam a...
• Separar preocupações (separation of concerns)
• Aumentar extensibilidade • Facilitar reuso
Tudo isso vale para aspectos
(crosscutting concerns)
Sem aspectospublic class Conta { private string numero; private double saldo; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) { if (valor > saldo) { throw new SaldoInsuficienteException)); } else { this.saldo = this.saldo - valor; } } public void Atualizar(Conta c) { this.numero = c.numero; this.saldo = c.saldo; } }
public class RepositorioContasAccess : RepositorioContas { public void Inserir(Conta conta) { string sql = "INSERT INTO Conta (NUMERO,SALDO) VALUES ('" + conta.Numero + "'," + conta.Saldo + ")"; OleDbCommand insertCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); insertCommand.ExecuteNonQuery(); } public void Atualizar(Conta conta) { string sql = "UPDATE Conta SET SALDO = (" + conta.Asldo + ") WHERE NUMERO = '" + conta.Numero + "'"; OleDbCommand updateCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); int linhasAfetadas; linhasAfetadas = updateCommand.ExecuteNonQuery(); if (linhasAfetadas == 0) { throw new ContaNaoEncontradaException(conta.Numero); } } }
public class CadastroContas { private RepositorioContas contas; public void Debitar(string numero, double valor) { Conta c = contas.Procurar(numero); c.Debitar(valor); contas.Atualizar(c); } public void Transferir(string numeroDe, string numeroPara, double valor) { Conta de = contas.Procurar(numeroDe); Conta para = contas.Procurar(numeroPara); de.Debitar(valor); para.Creditar(valor); contas.Atualizar(de); contas.Atualizar(para); } public double Saldo(string numero) { Conta c = contas.Procurar(numero); return c.Saldo; } }}
public class Banco { private CadastroContas contas; private Banco() { contas = new CadastroConta (new RepositorioContasAccess()); } public void Cadastrar(Conta conta) { Persistence.DBHandler.StartTransaction(); try { contas.Cadastrar(conta); Persistence.DBHandler.CommitTransaction(); } catch (System.Exception ex){ Persistence.DBHandler.RollBackTransaction(); throw ex; } } public void Transferir(string numeroDe, string numeroPara, double valor) { Persistence.DBHandler.StartTransaction(); try { contas.Transferir(numeroDe, numeroPara, valor); Persistence.DBHandler.CommitTransaction(); } catch (System.Exception ex){ Persistence.DBHandler.RollBackTransaction(); throw ex; } } }
public class DBHandler { private static OleDbConnection connection; public static OleDbConnection Connection { get { if (connection == null) { string dataSource = "BancoCS.mdb"; string strConexao = "Provider= Microsoft.Jet.OLEDB.4.0; " + "Data Source=" + dataSource; connection = new OleDbConnection(strConexao); } return connection; } } public static OleDbConnection GetOpenConnection() { Connection.Open(); return Connection; }
public static void StartTransaction() { Connection.Open(); transaction = Connection.BeginTransaction(); }
}
Com aspectospublic class Conta { private string numero; private double saldo; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) { if (valor > saldo) { throw new SaldoInsuficienteException)); } else { this.saldo = this.saldo - valor; } } public void Atualizar(Conta c) { this.numero = c.numero; this.saldo = c.saldo; } }
public class RepositorioContasAccess : RepositorioContas { public void Inserir(Conta conta) { string sql = "INSERT INTO Conta (NUMERO,SALDO) VALUES ('" + conta.Numero + "'," + conta.Saldo + ")"; OleDbCommand insertCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); insertCommand.ExecuteNonQuery(); } public void Atualizar(Conta conta) { string sql = "UPDATE Conta SET SALDO = (" + conta.Asldo + ") WHERE NUMERO = '" + conta.Numero + "'"; OleDbCommand updateCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); int linhasAfetadas; linhasAfetadas = updateCommand.ExecuteNonQuery(); if (linhasAfetadas == 0) { throw new ContaNaoEncontradaException(conta.Numero); } } }
public class CadastroContas { private RepositorioContas contas; public void Debitar(string numero, double valor) { Conta c = contas.Procurar(numero); c.Debitar(valor);} public void Transferir(string numeroDe, string numeroPara, double valor) { Conta de = contas.Procurar(numeroDe); Conta para = contas.Procurar(numeroPara); de.Debitar(valor); para.Creditar(valor);} public double Saldo(string numero) { Conta c = contas.Procurar(numero); return c.Saldo; } }}
public class Banco { private CadastroContas contas; private Banco() { contas = new CadastroConta} public void Cadastrar(Conta conta) {contas.Cadastrar(conta);} public void Transferir(string numeroDe, string numeroPara, double valor) {contas.Transferir(numeroDe, numeroPara, valor);} }
public class DBHandler { private static OleDbConnection connection; public static OleDbConnection Connection { get { if (connection == null) { string dataSource = "BancoCS.mdb"; string strConexao = "Provider= Microsoft.Jet.OLEDB.4.0; " + "Data Source=" + dataSource; connection = new OleDbConnection(strConexao); } return connection; } } public static OleDbConnection GetOpenConnection() { Connection.Open(); return Connection; }
public static void StartTransaction() { Connection.Open(); transaction = Connection.BeginTransaction(); }
}
public class Conta { private string numero; private double saldo; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) {
public class Conta { private string numero;; } public void Debitar(double valor) {
Código base
do sistema
Código do aspecto
de persistência
com OleDb
Roteiro
Problemas com implementações OO
Conceitos de OA e Eos Soluções baseadas em aspectos AspectC# e LOOM.NET Conclusões
Projeto OO ruim
G D COMUNICAÇÃO I D NEGÓCIO S
Problema: entrelaçamento de código comdiferentes propósitos
Projeto OO bom, em camadas
Interface com o usuário(GUI)
Comunicação
Negócio
Dados
Melhor, em camadas com PDC (sem interfaces)
CadastroClientes
cadastrar()
<<coleção de negócio>>
CadastroLogins
efeturaLogin()
<<coleção de negócio>>
Servidor do Banco
cadastrarCliente()cadastrarConta()efetuarLogin()
<<fachada de negócio>>
CadastroContas
cadastrar()descadastrar()procurar()creditar()transferir()atualizar()selecionarSaldosMenorQue()
<<coleção de negócio>>
RepositorioContasArray
inserir()retirar()procurar()atualizar()selecionarSaldosMenorQue()
<<coleção de dados>>
Conta[]
Conta
creditar()debitar()transferir()equals()atualizar()
<<classe básica de negócio>>
Camada de negócio
Camada dedados
Boa modularização sem persistência, distribuição, ...
public class Conta { private string numero; private double saldo; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) { if (valor > saldo) { throw new SaldoInsuficienteException)); } else { this.saldo = this.saldo - valor; } } public void Atualizar(Conta c) { this.numero = c.numero; this.saldo = c.saldo; } }
public class CadastroContas { private RepositorioContas contas; public void Debitar(string numero, double valor) { Conta c = contas.Procurar(numero); c.Debitar(valor);} public void Transferir(string numeroDe, string numeroPara, double valor) { Conta de = contas.Procurar(numeroDe); Conta para = contas.Procurar(numeroPara); de.Debitar(valor); para.Creditar(valor);} public double Saldo(string numero) { Conta c = contas.Procurar(numero); return c.Saldo; } }}
public class RepositorioContasArray : RepositorioContas { private Conta[] contas; private int indice; public RepositorioContasArray() { contas = new Conta[100]; indice = 0; } public void Inserir(Conta conta) { contas[indice] = conta; indice = indice + 1; } public void Atualizar(Conta conta) { int i = GetIndice(conta.Numero); if (i == indice) { throw new ContaNaoEncontradaException(conta.Numero); } else { contas[i].Atualizar(conta); } }
public class Programa { [STAThread] public static void Main(string[] args) { Banco fachada = Banco.GetInstance(); Programa.menu(fachada); } public static void menu(Banco fachada) { string numero = null; double valor = 0.0; Conta conta = null; int opcao = 1; while (opcao != 0) { try { System.Console.Out.WriteLine("Aperte <Enter> para continuar"); Util.Util.waitEnter(); System.Console.Out.WriteLine("\n\n\n\n\n\n\n"); System.Console.Out.WriteLine("Escolha uma das alternativas abaixo:"); System.Console.Out.WriteLine("1 - Cadastrar Conta"); System.Console.Out.WriteLine("2 - Creditar"); System.Console.Out.WriteLine("3 - Debitar"); System.Console.Out.WriteLine("4 - Transferir"); System.Console.Out.WriteLine("5 - Ver Saldo"); System.Console.Out.WriteLine("0 - Sair"); opcao = Util.Util.readInt(); switch (opcao) {
public class Banco { private CadastroContas contas; private Banco() { contas = new CadastroConta (new RepositorioContasAccess()); } public void Cadastrar(Conta conta) {contas.Cadastrar(conta);} public void Transferir(string numeroDe, string numeroPara, double valor) {contas.Transferir(numeroDe, numeroPara, valor);} }
Interface
Negócio Dados
Mas temos problemas com persistência via OleDb
public class Conta { private string numero; private double saldo; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) { if (valor > saldo) { throw new SaldoInsuficienteException)); } else { this.saldo = this.saldo - valor; } } public void Atualizar(Conta c) { this.numero = c.numero; this.saldo = c.saldo; } }
public class RepositorioContasAccess : RepositorioContas { public void Inserir(Conta conta) { string sql = "INSERT INTO Conta (NUMERO,SALDO) VALUES ('" + conta.Numero + "'," + conta.Saldo + ")"; OleDbCommand insertCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); insertCommand.ExecuteNonQuery(); } public void Atualizar(Conta conta) { string sql = "UPDATE Conta SET SALDO = (" + conta.Asldo + ") WHERE NUMERO = '" + conta.Numero + "'"; OleDbCommand updateCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); int linhasAfetadas; linhasAfetadas = updateCommand.ExecuteNonQuery(); if (linhasAfetadas == 0) { throw new ContaNaoEncontradaException(conta.Numero); } } }
public class CadastroContas { private RepositorioContas contas; public void Debitar(string numero, double valor) { Conta c = contas.Procurar(numero); c.Debitar(valor); contas.Atualizar(c); } public void Transferir(string numeroDe, string numeroPara, double valor) { Conta de = contas.Procurar(numeroDe); Conta para = contas.Procurar(numeroPara); de.Debitar(valor); para.Creditar(valor); contas.Atualizar(de); contas.Atualizar(para); } public double Saldo(string numero) { Conta c = contas.Procurar(numero); return c.Saldo; } }}
public class Banco { private CadastroContas contas; private Banco() { contas = new CadastroConta (new RepositorioContasAccess()); } public void Cadastrar(Conta conta) { Persistence.DBHandler.StartTransaction(); try { contas.Cadastrar(conta); Persistence.DBHandler.CommitTransaction(); } catch (System.Exception ex){ Persistence.DBHandler.RollBackTransaction(); throw ex; } } public void Transferir(string numeroDe, string numeroPara, double valor) { Persistence.DBHandler.StartTransaction(); try { contas.Transferir(numeroDe, numeroPara, valor); Persistence.DBHandler.CommitTransaction(); } catch (System.Exception ex){ Persistence.DBHandler.RollBackTransaction(); throw ex; } } }
public class DBHandler { private static OleDbConnection connection; public static OleDbConnection Connection { get { if (connection == null) { string dataSource = "BancoCS.mdb"; string strConexao = "Provider= Microsoft.Jet.OLEDB.4.0; " + "Data Source=" + dataSource; connection = new OleDbConnection(strConexao); } return connection; } }
public static OleDbConnection GetOpenConnection() { Connection.Open(); return Connection; }
public static void StartTransaction() { Connection.Open(); transaction = Connection.BeginTransaction(); }
}
Código OleDb em vermelho…
Problemas com implementação OO
public class CadastroContas { private RepositorioContas contas; public void Debitar(string numero, double valor) { Conta c = contas.Procurar(numero); c.Debitar(valor); contas.Atualizar(c); } public void Transferir(string numeroDe, string numeroPara, double valor) { Conta de = contas.Procurar(numeroDe); Conta para = contas.Procurar(numeroPara); de.Debitar(valor); para.Creditar(valor); contas.Atualizar(de); contas.Atualizar(para); } public double Saldo(string numero) { Conta c = contas.Procurar(numero); return c.Saldo; } }}
public class Banco { private CadastroContas contas; private Banco() { contas = new CadastroConta (new RepositorioContasAccess()); } public void Cadastrar(Conta conta) { Persistence.DBHandler.StartTransaction(); try { contas.Cadastrar(conta); Persistence.DBHandler.CommitTransaction(); } catch (System.Exception ex){ Persistence.DBHandler.RollBackTransaction(); throw ex; } } public void Transferir(string numeroDe, string numeroPara, double valor) { Persistence.DBHandler.StartTransaction(); try { contas.Transferir(numeroDe, numeroPara, valor); Persistence.DBHandler.CommitTransaction(); } catch (System.Exception ex){ Persistence.DBHandler.RollBackTransaction(); throw ex; } } }
public class DBHandler { private static OleDbConnection connection; public static OleDbConnection Connection { get { if (connection == null) { string dataSource = "BancoCS.mdb"; string strConexao = "Provider= Microsoft.Jet.OLEDB.4.0; " + "Data Source=" + dataSource; connection = new OleDbConnection(strConexao); } return connection; } }
public static OleDbConnection GetOpenConnection() { Connection.Open(); return Connection; }
public static void StartTransaction() { Connection.Open(); transaction = Connection.BeginTransaction(); }
}
Espalhamento de código(scattering)
Entrelaçamento de código(tangling)
Persistência na fachadapublic class Banco { private CadastroContas contas;... public void Cadastrar(Conta conta) { try {Pers.DBHandler.StartTransaction(); contas.Cadastrar(conta); ... Pers.DBHandler.CommitTransaction(); } catch (System.Exception ex){ Pers.DBHandler.RollBackTransaction();... } }... Código de negócio misturado
com transações (não pode ficar na coleção de dados)
Persistência na coleção de negóciopublic class CadastroContas { private RepositorioContas contas;... public void Creditar(string numero, double valor) { Conta c = contas.Procurar(numero); c.Creditar(valor); contas.Atualizar(c); }... Sincronização de
estados entre objetos e tabelas (registros)
Problemas e conseqüências Código de persistência misturado
com código de negócio Código de persistência aparece em
várias classes Código difícil de manter e reusar
• mudanças na tecnologia de persistência serão invasivas
Implementação OApublic class Conta { private string numero; private double saldo; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) { if (valor > saldo) { throw new SaldoInsuficienteException)); } else { this.saldo = this.saldo - valor; } } public void Atualizar(Conta c) { this.numero = c.numero; this.saldo = c.saldo; } }
public class RepositorioContasAccess : RepositorioContas { public void Inserir(Conta conta) { string sql = "INSERT INTO Conta (NUMERO,SALDO) VALUES ('" + conta.Numero + "'," + conta.Saldo + ")"; OleDbCommand insertCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); insertCommand.ExecuteNonQuery(); } public void Atualizar(Conta conta) { string sql = "UPDATE Conta SET SALDO = (" + conta.Asldo + ") WHERE NUMERO = '" + conta.Numero + "'"; OleDbCommand updateCommand = new OleDbCommand(sql,DBHandler.Connection,DBHandler.Transaction); int linhasAfetadas; linhasAfetadas = updateCommand.ExecuteNonQuery(); if (linhasAfetadas == 0) { throw new ContaNaoEncontradaException(conta.Numero); } } }
public class CadastroContas { private RepositorioContas contas; public void Debitar(string numero, double valor) { Conta c = contas.Procurar(numero); c.Debitar(valor);} public void Transferir(string numeroDe, string numeroPara, double valor) { Conta de = contas.Procurar(numeroDe); Conta para = contas.Procurar(numeroPara); de.Debitar(valor); para.Creditar(valor);} public double Saldo(string numero) { Conta c = contas.Procurar(numero); return c.Saldo; } }}
public class Banco { private CadastroContas contas; private Banco() { contas = new CadastroConta} public void Cadastrar(Conta conta) {contas.Cadastrar(conta);} public void Transferir(string numeroDe, string numeroPara, double valor) {contas.Transferir(numeroDe, numeroPara, valor);} }
public class DBHandler { private static OleDbConnection connection; public static OleDbConnection Connection { get { if (connection == null) { string dataSource = "BancoCS.mdb"; string strConexao = "Provider= Microsoft.Jet.OLEDB.4.0; " + "Data Source=" + dataSource; connection = new OleDbConnection(strConexao); } return connection; } } public static OleDbConnection GetOpenConnection() { Connection.Open(); return Connection; }
public static void StartTransaction() { Connection.Open(); transaction = Connection.BeginTransaction(); }
}
public class Conta { private string numero; private double saldo; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) {
public class Conta { private string numero;; } public void Debitar(double valor) {
Código base
do sistema
Código do aspecto
de persistência
com OleDb
Conseqüências Melhor modularidade, reuso e
extensibidade• mais unidades de código• mudanças na base podem causar
impacto nos aspectos Separation of concerns
• relação entre os aspectos e o resto do sistema nem sempre é clara
Normalmente menos linhas de código
modularidade?
Weaving é usado para… Compor a base do sistema com os
aspectos
A B
Tecnologia de distribução
Sistema original chamadas locais entre A e B
Sistema distribuído chamadas remotas entre A e B
WeaverProcesso decomposição
Aspectos dedistribuição
A B
Composição nos join points
object A
object B
and returns or throws
a method is called
dispatch
dispatch
a method is called
and returns or throwsa method executes
and returnsor throws
a method executesand returns or throws
Comportamento pode ser alterado nos join points…
Fonte: AspectJ Programming Guide
Pointcuts especificam join points Identificam joint points de um
sistema• chamadas e execuções de métodos (e
construtores)• acessos a atributos• tratamento de exceções• inicialização estática e dinâmica
Composição de joint points • &&, || e !
Identificando chamadas de métodos
pointcut writeCall():
call(public void any.Write(string));
com argumento
string
método Write de qualquer
classe
identifica chamadas
de …
nome do pointcut
Advice especifica comportamento extra nos join points Define código adicional que deve
ser executado…• before• after
—after returning—after throwing
• ou around
join points
Alterando o comportamento de chamadas de métodos
after(): writeCall() && within(HelloWorld)
{
System.Console.Write(“ AOP World");
}
após...
qualquer chamada a Write dentro de
HelloWorld
a ação especificada será
executada
Aspectos agrupam pointcuts, advices, propriedades, etc.aspect HelloAOPWorld {
pointcut writeCall():
call(public void any.Write(string));
after():
writeCall() && within(HelloWorld) {
System.Console.Write(“ AOP World!");
}
}
Hello AOP World!
public class HelloWorld {
public static void Main(string[] args) {
System.Console.Write(“Hello");
}
} Chamada afetada pelo advice, caso
HelloAOPWorld tenha sido composto
com HelloWorld
Aspecto de persistência, advicespublic aspect PersistenceAspect {
before(): TransMethods() {
Pers.DBHandler.StartTransaction();
}
after() returning(): TransMethods() {
Pers.DBHandler.CommitTransaction();
}
after() throwing(): TransMethods() {
Pers.DBHandler.RollBackTransaction();
}...
Além de dynamic crosscutting com advice… Temos também static
crosscutting• alterar relação de subtipo• adicionar membros a classes
Inter-typedeclarations
Aspecto de persistência, pointcut
pointcut TransMethods():
execution(public any Trans.any(..));
private interface Trans {
public void Cadastrar(Conta conta);...
}
declare parents: Banco:Trans;
altera a hierarquia
de tipos
interface local
ao aspecto
call versus execution
Aspecto de persistência, pointcut, alternativa
pointcut TransMethods():
execution(public any Banco.Cadastrar(..)) ||
execution(public any Banco.Creditar(..)) ||
execution(public any Banco.Debitar(..)) ||
...
Declaração entre tipos no aspecto de distribuição
public SaldoInsuficienteException.new(
...SerializationInfo info,
...StreamingContext context):
base(info,context) {
numero = info.GetString("numero");
saldo = info.GetDouble("saldo");
}
Introduz construtor na classe indicada
Banco com controle de concorrência básico
public class Conta { private string numero; private double saldo;Private long timestamp; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) { if (valor > saldo) { throw new SaldoInsuficienteException)); } else { this.saldo = this.saldo - valor; } } public void Atualizar(Conta c) { this.numero = c.numero; this.saldo = c.saldo;this.timestamp = c.timestamp; } }
public class CadastroContas { private RepositorioContas contas; public void Debitar(string numero, double valor) { lock(this) { Conta c = contas.Procurar(numero); c.Debitar(valor);}} public void Transferir(string numeroDe, string numeroPara, double valor) {lock(this) { Conta de = contas.Procurar(numeroDe); Conta para = contas.Procurar(numeroPara); de.Debitar(valor); para.Creditar(valor);}} public double Saldo(string numero) { Conta c = contas.Procurar(numero); return c.Saldo; } }}
public class RepositorioContasArray : RepositorioContas { private Conta[] contas; private int indice; public RepositorioContasArray() { contas = new Conta[100]; indice = 0; } public void Inserir(Conta conta) { contas[indice] = conta; indice = indice + 1; } public void Atualizar(Conta conta) { int i = GetIndice(conta.Numero); if (i == indice) { throw new ContaNaoEncontradaException(conta.Numero); } else { if (conta.Timestamp == contas[I].Timestamp) { contas[i].Atualizar(conta);Conta.incTimestamp();} else {………….}} } }
public class Banco { private CadastroContas contas; private Banco() { contas = new CadastroConta (new RepositorioContasAccess()); } public void Cadastrar(Conta conta) {contas.Cadastrar(conta);} public void Transferir(string numeroDe, string numeroPara, double valor) {contas.Transferir(numeroDe, numeroPara, valor);} }
Dados
Negócio Concorrência
Controle de concorrência na coleção de negócio
public void Debitar(string n, double v) { lock(this) {... Conta c = contas.Procurar(n); ... c.Debitar(v); }}... Controle de concorrência para
evitar interferências indesejadas
entre os métodos da fachada
Controle de concorrência na coleção de dadospublic void Atualizar(Conta conta) { ... c = contas[i]; if (conta.Timestamp == c.Timestamp) { c.Atualizar(conta); c.UpdateTimestamp(); } else {...}
}... Controle de concorrência para
evitar manipulação de versões
desatualizadas de objetos
Controle de concorrência na classe básicapublic class Conta { private double saldo;... private long timestamp; public void Atualizar(Conta c) { this.saldo = c.saldo;... this.timestamp = c.timestamp; }... }
Controle otimista de concorrência
Os pointcuts podem expor o contexto dos join points Informações disponíveis nos join
points• argumentos de métodos• objetos responsáveis pela execução• objetos alvo• variáveis de instância
Aspecto de concorrência, pointcut
public aspect ConcurrencyAspect {
pointcut ConcurrentMethods(object t):
((call(void CadContas.Cadastrar(Conta))
&& target(t)) || ...
);
Informação exposta
Associação de t com o alvo da chamada de
método
Aspecto de concorrência, adviceObject around(Object t):
ConcurrentMethods(t) {
Object obj;
lock (t) {
obj = proceed(t);
}
return obj;
}
O corpo do around
poderá usar a informação
exposta
A execução do join point
interceptado deve
continuar
Aspecto de persistência, outro advice
void around(Banco b):
fset(CadastroContas Banco.contas) &&
execution(private Banco.new()) && this(b) {
b.contas = new CadastroContas(
new RepositorioContasAccess());
}
atribuições à variável de instância ou
estática
Associação de b com o objeto sendo inicializado
Quebra de encapsulamento
void around(Banco b):
fset(CadastroContas Banco.contas) &&
execution(private Banco.new()) && this(b) {
b.contas = new CadastroContas(
new RepositorioContasAccess());
}
Variável de instância privada! Aspecto deve ser definido como privileged
Banco remoto com .NET Remoting
[System.Serializable()]public class Conta { private string numero; private double saldo; public void Creditar(double valor) { this.saldo = this.saldo + valor; } public void Debitar(double valor) { if (valor > saldo) { throw new SaldoInsuficienteException)); } else { this.saldo = this.saldo - valor; } } public void Atualizar(Conta c) { this.numero = c.numero; this.saldo = c.saldo; } }
public class RepositorioContasArray : RepositorioContas { private Conta[] contas; private int indice; public RepositorioContasArray() { contas = new Conta[100]; indice = 0; } public void Inserir(Conta conta) { contas[indice] = conta; indice = indice + 1; } public void Atualizar(Conta conta) { int i = GetIndice(conta.Numero); if (i == indice) { throw new ContaNaoEncontradaException(conta.Numero); } else { contas[i].Atualizar(conta); } }
public class Programa { [STAThread] public static void Main(string[] args) { Banco fachada; try { TcpChannel chan = new TcpChannel(); (Banco)Activator.GetObject(typeof(Fachada.Banco),System.Console.WriteLine("Could not locate server"); } else { Programa.menu(fachada); } } catch (Exception e) {System.Console.WriteLine("The error was: " + e.Message); } } public static void menu(Banco fachada) { string numero = null;while (opcao != 0) { try { System.Console.Out.WriteLine("Aperte <Enter> para continuar"); Util.Util.waitEnter(); System.Console.Out.WriteLine("\n\n\n\n\n\n\n");System.Console.Out.WriteLine("2 - Creditar"); …} catch (Exception exception) { System.Console.Out.WriteLine(exception.Message); }
public class Banco: System.MarshalByRefObject { private CadastroContas contas; private Banco() { contas = new CadastroConta (new RepositorioContasAccess()); } public void Cadastrar(Conta conta) {contas.Cadastrar(conta);} public void Transferir(string numeroDe, string numeroPara, double valor) {contas.Transferir(numeroDe, numeroPara, valor);} }
public class CadastroContas { private RepositorioContas contas; public void Debitar(string numero, double valor) { Conta c = contas.Procurar(numero); c.Debitar(valor); contas.Atualizar(c); } public void Transferir(string numeroDe, string numeroPara, double valor) { Conta de = contas.Procurar(numeroDe); Conta para = contas.Procurar(numeroPara); de.Debitar(valor); para.Creditar(valor); contas.Atualizar(de); contas.Atualizar(para); } public double Saldo(string numero) { Conta c = contas.Procurar(numero); return c.Saldo; } }}
public class ServerInit {
public static void Main(string[] args) {
try {
TcpChannel chan = new TcpChannel(8085);
ChannelServices.RegisterChannel(chan);
RemotingConfiguration.RegisterWellKnownServiceType
(Type.GetType("Fachada.Banco"),
} catch (Exception e) {
Console.WriteLine("An error has happened:");
Console.WriteLine(e.Message);
}
}
[System.Serializable()]
public class ContaJaCadastradaException :
System.Exception {
public string Numero {
get { return numero;
} }
private string numero;
public ContaJaCadastradaException(numero;
}
public ContaJaCadastradaExceptionnumero =
info.GetString("numero");
}
public override void GetObjectData(base.
GetObjectData(info,context);
info.AddValue("numero", numero,typeo
f(string));
}}
Interface
Negócio
Distribuição
Dados
Distribuição na interface com o usuário
public static void Main(string[] args) { Banco fachada; try {... fachada = (Banco) Activator.GetObject( typeof(Fachada.Banco), "tcp://localhost:8085/BancoRemoto"); ... Programa.menu(fachada); } catch...}
serviço de
lookup
Distribuição na fachada
public class Banco: System.MarshalByRefObject {
private CadastroContas contas;private static Banco banco;
Supertipo de qualquer servidor
Manipulação de objetos por valor
versus por referência
Distribuição na classe básica e exceções
[System.Serializable()]public class Conta {
private double saldo;...}[System.Serializable()]
public class ContaNaoEncontradaException
: System.Exception {...}
Possibilita a passagem de objetos por valor
Aspecto de distribuição, servidorpublic aspect DistributionAspectServer {
declare parents:
Banco:System.MarshalByRefObject;
}
Temos dois aspectos apenas por questões de
implantação
Aspecto de distribuição, cliente
aspect DistributionAspectClient {
declare class attributes:
(Conta || SaldoInsuficienteException)
[System.Serializable()];
...
Acrescenta atributos a uma classe
Aspecto de distribuiçãovoid around():
execution(...void Programa.Main(string[])) {
Banco fachada;
try {...
fachada = (Banco) Activator.GetObject(
typeof(Fachada.Banco),
"tcp://localhost:8085/BancoRemoto");
...Programa.menu(fachada);
} catch...
}
O aspecto de sincronização de estado é... Útil tanto para persistência quanto
para distribuição Dividido em duas partes:
• registro de objetos modificados (sujos)
• atualização dos objetos modificados
Registro de objetos modificados
pointcut localUpdate(Conta c):
this(CadContas) && target(c) &&
(call(any Conta.Creditar(..)) || ...;
private ArrayList dirtyObjects = ...;
after(Conta c) returning(): localUpdate(c) {
dirtyObjects.add(c);
}
Quaisquer argumentos
Atualização dos objetos modificadospointcut localExecution(CadContas cad):
if(HasDirtyObjects()) && this(cad) &&
execution(public any any(..));
after(CadContas cad) returning():
localExecution(cad) {
foreach (Conta c in dirtyObjects) {
cad.Contas.Atualizar(c);
}
}
Aspecto de sincronização de estado
privileged aspect StateSynchronization
percflow(execution(any Banco.any(..))) {
RepositorioContas CadastroContas.Contas {
get {
return this.contas;
}
}
publics omitidos...
Para evitar interferências
no atributo do aspectoAdiciona propriedade
Instâncias de aspectos
default, apenas uma instância, aspecto estático
percflow, uma instância para cada fluxo de um joint point
pertarget, uma instância para cada alvo de um joint point
pertthis percflowbelow
cflow(<pointcut>)• todos os join points no fluxo de controle do
<pointcut> cflowbelow(<pointcut>)
• todos os join points no fluxo de controle do <pointcut>, exceto o inicial
fget(<signature>) • todos os join points dos acessos às variáveis
com assinatura <signature>
Outros pointcut designators
withincode(<method>) • todos os join points do código que aparece
dentro de um método initialization(<constructor>)
• todos os join points de inicialização com uma dada assinatura
args(<Type or Id, ...>)• todos os join points com argumentos dos
tipos especificados
Mais pointcut designators
NomeAspecto.aspectOf() • retorna a instância de um
determinado aspecto, útil para acesso a membros do aspecto
declare precedence: <TypeList> • define a precedência entre
aspectos que afetam um mesmo join point
Mais construções de Eos
Advices só para instâncias específicas instancelevel
• modificador de advice e aspecto• advice ou aspecto só afetará
instâncias registradas pelos métodos abaixo
addObject removeObject
Selecionando instâncias
Fonte: Eos distribution
public aspect Trace {
public void Select(Bit bit) {
addObject(bit);
}
after():execution(public any any.any()) {
Console.WriteLine("In any method");
}
instancelevel after():
execution(public bool Bit.Get()) {
Console.WriteLine(“A selective advice");
}...
Sincronização entre conjuntos, aspecto
Fonte: Eos distribution
public instancelevel aspect Bijection {
bool busy;
Set A;
Set B;
public void Relate(Set A, Set B) {
addObject(A); addObject(B);
this.A = A; this.B = B;
}
Sincronização entre conjuntos, advice
after():execution(public bool Set.Insert()) {
...r = (bool) thisJoinPoint.getReturnValue();
if(r) {
Set set = (Set) thisJoinPoint.getTarget();
object[] args = thisJoinPoint.getArgs();
Element e = (Element)arguments[0];
if (set == this.A) B.Insert(e);
else A.Insert(e);
}
}...
Fonte: Eos distribution
Sincronização entre conjuntos, inicialização
Fonte: Eos distribution
public static void Main (string[] argument) {
Set A = new Set();
Set B = new Set();
Bijection bj = new Bijection();
bj.Relate(A,B);
Element A1 = new Element("A1");
A.Insert(A1);
Element B1 = new Element("B1");
B.Insert(B1);...
}
Acessando mais informações dos join pointsAlém das informações expostas pelos pointcuts, é possível acessar mais sobre os join points usando referências especiais:
•thisJoinPoint
•thisJoinPointStaticPart
Métodos das referências especiais
thisJoinPoint
getArgs()
getTarget()
getReturnValue()
thisJoinPointStaticPart
getSignature()
Debugging ou logging simplespublic aspect LoggingAspect {
before(): execution(public any any.any(..)) {
System.Console.Write("A method from..." +
thisJoinPoint.getTarget() + " is about" +
“ to be executed. Its signature is " +
thisJoinPointStaticPart.getSignature());
}
Aspecto de desenvolvimento versus produção
after(): execution(public any any.any(..)) {
System.Console.Write("Now the method" +
... + " has finished"); ...
System.Console.Write("The return value was “
+ thisJoinPoint.getReturnValue());
}
Abordagens para aspectos com C# Eos
• baseada em uma extensão de C# com novos recursos linguísticos
AspectC#• baseada em XMLs que descrevem
como classes C# devem ser compostas LOOM.NET
• baseada em API e atributos C# que descrevem como classes C# devem ser compostas
Orientação a aspectos é...
Quantificação (quantification)• uma parte do programa tem efeito em
várias outras• o aspecto afeta várias classes e outros
aspectos Mudanças não invasivas
(obliviouness)• uma parte A do programa tem efeito
sobre uma B sem precisar alterar o código de B
AspectC#
Advices disponíveis: before, after e around
Acesso reflexivo ao contexto do join point
Falta quantificação
AspectC#, localização da base e dos aspectos
<Aspect>
<TargetBase>c:\...</TargetBase>
<AspectBase>c:\...</AspectBase>
AspectC#, definição do aspecto (advice)...
<Aspect-Method>
<Name>AspectoSincronizacao</Name>
<Namespace>Contas</Namespace>
<Class>AspectoSincronizacao</Class>
<Method>Sincronizar()</Method>
</Aspect-Method>
AspectC#, definição da classe afetada (pointcut)...<Target>
<Namespace>Contas</Namespace>
<Class>CadContas</Class>
<Method>
<Name>Cadastrar()</Name>
<Type>before</Type>
<Aspect-Name>AspectoSincronizacao
</Aspect-Name></Method></Target></Aspect>
LOOM.NET
Advices disponíveis: before, after e instead (around) • proceed(context)
Aspectos são classes que herdam de Aspect
Suporte a quantificação (wildcards) É invasiva
LOOM.NET, invasãopublic class HelloWorld {
public virtual void SayHello() {
System.Console.WriteLine("Hello World");
}
public virtual void SayBye() {
System.Console.WriteLine("Bye World");
}
}Métodos afetados têm
que ser virtual
LOOM.NET, aspecto
public class LoggingAspect:Aspect {
[Call(Invoke.After)]
public void SayBye() {
System.Console.Write("SayBye! <-");
}...Corpo do método é o
corpo do advice; atributo e nome do método são o pointcut e tipo do advice
LOOM.NET, quantificação
[Call(Invoke.Before,Alias="Say*")]
public void Say() {
System.Console.Write(“-> SayHello!");
}
Nome do método deixa de contribuir para o
pointcut
LOOM.NET, mais invasãoclass MainClass {
[STAThread]
public static void Main(string[] args) {
HelloWorld h = (HelloWorld)
Weaver.CreateInstance(
typeof(HelloWorld),
null,new LoggingAspect());
h.SayHello();...
}...
AOP ou um bom projeto OO?
Decorator ou Adapter
Com padrões (adapter ou decorator)… Escrevemos mais código A ligação entre o adaptador e o
objeto adaptado• é explicita e invasiva• não altera o comportamento de
chamadas internas para o objeto adaptado
• não tem acesso ao objeto fonte (source)
• pode ser modificado dinamicamente
Reuso e extensibilidade de aspectos via padrões e frameworks Tag interface
• como na interface Trans do aspecto de transações
Glue aspects• advices invocando serviços auxiliares
Template pointcut• com aspectos abstratos
Aspecto abstrato para persistênciapublic abstract aspect APersistenceAspect {
abstract pointcut TransactionalMethods();
before(): TransactionalMethods() {
Pers.DBHandler.StartTransaction();
}...
}
Aspecto concreto para persistência
public aspect PersistenceAspect:
APersistenceAspect {
private interface Trans {
public void Cadastrar(Conta conta);...
}
declare parents: Banco:Trans;
override pointcut TransactionalMethods():
execution(public any Trans.any(..));
}
Aspectos, pontos positivos Útil para implementar crosscutting
concerns Modularidade, reuso, e
extensibilidade de software Produtividade Separação na implementação e
testes (plug-in/out)
Aspectos, pontos negativos Modularidade relativa
• falta noção de interface Dependência entre classes e
aspectos • sensível a refactorings
Necessidade de refactorings para expor join points
É essencial usar IDE Conflitos entre aspectos
Tópicos de pesquisa
Orientação a aspectos modular• entendimento das partes leva ao
entendimento do todo; não é preciso expandir...
Novos joint points Processo de desenvolvimento Linhas de produtos de software
Referências
Ver roteiro de exercícios ... [email protected]