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
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 Banco {
public class CadastroContas {
private CadastroContas contas;
private RepositorioContas contas;
private Banco() {
contas = new CadastroConta
(new RepositorioContasAccess());
}
public void Debitar(string numero, double valor) {
Conta c = contas.Procurar(numero);
c.Debitar(valor);
contas.Atualizar(c);
}
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 n
umeroPara, double valor) {
Persistence.DBHandler.StartTransaction();
try {
contas.Transferir(numeroDe, numeroPara, valor);
Persistence.DBHandler.CommitTransaction();
} catch (System.Exception ex){
Persistence.DBHandler.RollBackTransaction();
throw ex;
}
}
public void Transferir(string numeroDe, string n
umeroPara, 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;
}
}
}
}
Código de persistência
em vermelho…
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.As
ldo + ") WHERE NUMERO = '" + conta.Numero + "'";
OleDbCommand updateCommand = new OleDbCommand(s
ql,DBHandler.Connection,DBHandler.Transaction);
int linhasAfetadas;
linhasAfetadas = updateCommand.ExecuteNonQuery();
if (linhasAfetadas == 0) {
throw new ContaNaoEncontradaException(conta.Numero);
}
}
}
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) {
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;
}
}
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 aspectos
public class Banco {
private RepositorioContas contas;
private Banco() {
contas = new CadastroConta
(new RepositorioContasAccess());
}
public void Debitar(string numero, double valor) {
Conta c = contas.Procurar(numero);
c.Debitar(valor);
contas.Atualizar(c);
}
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 n
umeroPara, 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 CadastroContas {
private CadastroContas contas;
public void Transferir(string numeroDe, string n
umeroPara, 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 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.As
ldo + ") WHERE NUMERO = '" + conta.Numero + "'";
OleDbCommand updateCommand = new OleDbCommand(s
ql,DBHandler.Connection,DBHandler.Transaction);
int linhasAfetadas;
linhasAfetadas = updateCommand.ExecuteNonQuery();
if (linhasAfetadas == 0) {
throw new ContaNaoEncontradaException(conta.Numero);
}
}
}
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) {
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;
}
}
Com aspectos
public class Banco {
public class CadastroContas {
private CadastroContas contas;
private RepositorioContas contas;
private Banco() {
contas = new CadastroConta
public void Debitar(string numero, double valor) {
Conta c = contas.Procurar(numero);
c.Debitar(valor);
}
}
public void Cadastrar(Conta conta) {
contas.Cadastrar(conta);
}
public void Transferir(string numeroDe, string n
umeroPara, double valor) {
contas.Transferir(numeroDe, numeroPara, valor);
}
}
public void Transferir(string numeroDe, string n
umeroPara, 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 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.As
ldo + ") WHERE NUMERO = '" + conta.Numero + "'";
OleDbCommand updateCommand = new OleDbCommand(s
ql,DBHandler.Connection,DBHandler.Transaction);
int linhasAfetadas;
linhasAfetadas = updateCommand.ExecuteNonQuery();
if (linhasAfetadas == 0) {
throw new ContaNaoEncontradaException(conta.Numero);
}
}
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 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 com
diferentes 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)
Camada de
negócio
Camada de
dados
Boa modularização sem
persistência, distribuição, ...
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) {
Dados
public class Banco {
private RepositorioContas contas;
private Banco() {
contas = new CadastroConta
(new RepositorioContasAccess());
}
public void Debitar(string numero, double valor) {
Conta c = contas.Procurar(numero);
c.Debitar(valor);
public void Cadastrar(Conta conta) {
contas.Cadastrar(conta);
}
public void Transferir(string numeroDe, string n
umeroPara, double valor) {
contas.Transferir(numeroDe, numeroPara, valor);
}
}
public class CadastroContas {
private CadastroContas contas;
}
public void Transferir(string numeroDe, string n
umeroPara, 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);
}
}
Interface
Negócio
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;
}
}
Mas temos problemas com
persistência via OleDb
public class Banco {
public class CadastroContas {
private CadastroContas contas;
private RepositorioContas contas;
private Banco() {
contas = new CadastroConta
(new RepositorioContasAccess());
}
public void Debitar(string numero, double valor) {
Conta c = contas.Procurar(numero);
c.Debitar(valor);
contas.Atualizar(c);
}
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 n
umeroPara, double valor) {
Persistence.DBHandler.StartTransaction();
try {
contas.Transferir(numeroDe, numeroPara, valor);
Persistence.DBHandler.CommitTransaction();
} catch (System.Exception ex){
Persistence.DBHandler.RollBackTransaction();
throw ex;
}
}
public void Transferir(string numeroDe, string n
umeroPara, 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;
}
}
}
}
Código OleDb em
vermelho…
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.As
ldo + ") WHERE NUMERO = '" + conta.Numero + "'";
OleDbCommand updateCommand = new OleDbCommand(s
ql,DBHandler.Connection,DBHandler.Transaction);
int linhasAfetadas;
linhasAfetadas = updateCommand.ExecuteNonQuery();
if (linhasAfetadas == 0) {
throw new ContaNaoEncontradaException(conta.Numero);
}
}
}
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) {
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;
}
}
Problemas com
implementação OO
public class Banco {
public class CadastroContas {
private CadastroContas contas;
private RepositorioContas contas;
private Banco() {
contas = new CadastroConta
(new RepositorioContasAccess());
}
public void Debitar(string numero, double valor) {
Conta c = contas.Procurar(numero);
c.Debitar(valor);
contas.Atualizar(c);
}
public void Cadastrar(Conta conta) {
Persistence.DBHandler.StartTransaction();
try {
contas.Cadastrar(conta);
Persistence.DBHandler.CommitTransaction();
} catch (System.Exception ex){
Persistence.DBHandler.RollBackTransaction();
throw ex;
}
}
Entrelaçamento
de código
(tangling)
public void Transferir(string numeroDe, string n
umeroPara, double valor) {
Persistence.DBHandler.StartTransaction();
try {
contas.Transferir(numeroDe, numeroPara, valor);
Persistence.DBHandler.CommitTransaction();
} catch (System.Exception ex){
Persistence.DBHandler.RollBackTransaction();
throw ex;
}
}
public void Transferir(string numeroDe, string n
umeroPara, 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 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)
Persistência na fachada
public 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ócio
public 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 OA
public class Banco {
public class CadastroContas {
private CadastroContas contas;
private RepositorioContas contas;
private Banco() {
contas = new CadastroConta
public void Debitar(string numero, double valor) {
Conta c = contas.Procurar(numero);
c.Debitar(valor);
}
}
public void Cadastrar(Conta conta) {
contas.Cadastrar(conta);
}
public void Transferir(string numeroDe, string n
umeroPara, double valor) {
contas.Transferir(numeroDe, numeroPara, valor);
}
}
public void Transferir(string numeroDe, string n
umeroPara, 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 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.As
ldo + ") WHERE NUMERO = '" + conta.Numero + "'";
OleDbCommand updateCommand = new OleDbCommand(s
ql,DBHandler.Connection,DBHandler.Transaction);
int linhasAfetadas;
linhasAfetadas = updateCommand.ExecuteNonQuery();
if (linhasAfetadas == 0) {
throw new ContaNaoEncontradaException(conta.Numero);
}
}
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 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
modularidade?
• 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
Weaving é usado para…
Compor a base do sistema com os
aspectos
Sistema original
chamadas locais entre A e B
A
Processo de
composição
Sistema distribuído
chamadas remotas entre A e B
Aspectos de
distribuição
B
Weaver
A
B
Tecnologia de distribução
Composição nos join
points
a method is called
and returns or
throws
object A
a method is
called
and returns
or throws
dispatch
object B
dispatch
a method executes
and returns or throws
Comportamento pode
ser alterado nos join
points…
a method executes
and returns or throws
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
nome do
pointcut
pointcut writeCall():
call(public void any.Write(string));
identifica
chamadas de
…
método Write
de qualquer
classe
com
argumento
string
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
após...
qualquer chamada a
Write dentro de
HelloWorld
after(): writeCall() && within(HelloWorld)
{
System.Console.Write(“ AOP World");
}
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,
advices
public aspect PersistenceAspect {
before(): TransMethods() {
Pers.DBHandler.StartTransaction();
}
after() returning(): TransMethods() {
Pers.DBHandler.CommitTransaction();
}
after() throwing(): TransMethods() {
Pers.DBHandler.RollBackTransaction();
}...
dynamic
crosscutting com advice…
Além de
Temos também static
crosscutting
• alterar relação de subtipo
• adicionar membros a classes
Inter-type
declarations
Aspecto de persistência,
pointcut
call versus execution
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
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
Introduz construtor
na classe indicada
public SaldoInsuficienteException.new(
...SerializationInfo info,
...StreamingContext context):
base(info,context) {
numero = info.GetString("numero");
saldo = info.GetDouble("saldo");
}
Banco com controle de
concorrência básico
public class Banco {
public class CadastroContas {
private CadastroContas contas;
private RepositorioContas contas;
private Banco() {
contas = new CadastroConta
(new RepositorioContasAccess());
}
public void Debitar(string numero, double valor) {
lock(this) {
Conta c = contas.Procurar(numero);
c.Debitar(valor);
public void Cadastrar(Conta conta) {
contas.Cadastrar(conta);
}
public void Transferir(string numeroDe, string n
umeroPara, double valor) {
contas.Transferir(numeroDe, numeroPara, valor);
}
}
}
public void Transferir(string numeroDe, string n
umeroPara, 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;
}
}
}
Negócio
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 {………….}}
}
}
Dados
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;
}
}
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 dados
public 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ásica
public 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
Informação
exposta
public aspect ConcurrencyAspect {
pointcut ConcurrentMethods(object t):
((call(void CadContas.Cadastrar(Conta))
&& target(t)) || ...
);
Associação de t com
o alvo da chamada de
método
Aspecto de concorrência,
advice
O corpo do
Object around(Object t):
ConcurrentMethods(t) {
Object obj;
around poderá
usar a
informação
exposta
lock (t) {
obj = proceed(t);
}
return obj;
}
A execução do
join point
interceptado
deve continuar
Aspecto de persistência,
outro advice
atribuições à
void around(Banco b):
variável de
instância ou
estática
fset(CadastroContas Banco.contas) &&
execution(private Banco.new()) && this(b) {
b.contas = new CadastroContas(
new RepositorioContasAccess());
}
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
Distribuição
public class Programa {
Interface
Negócio
public class Banco: System.MarshalByRefObject {
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 CadastroContas {
private CadastroContas contas;
private RepositorioContas contas;
private Banco() {
contas = new CadastroConta
(new RepositorioContasAccess());
}
public void Debitar(string numero, double valor) {
Conta c = contas.Procurar(numero);
c.Debitar(valor);
contas.Atualizar(c);
}
public void Cadastrar(Conta conta) {
contas.Cadastrar(conta);
}
public void Transferir(string numeroDe, string n
umeroPara, double valor) {
contas.Transferir(numeroDe, numeroPara, valor);
}
}
[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 void Transferir(string numeroDe, string n
umeroPara, 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);
}
}
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);
}
}
Dados
[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;
}
}
[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));
}}
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;
Manipulação de
objetos por valor
versus por
referência
Supertipo de
qualquer servidor
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,
servidor
public aspect DistributionAspectServer {
declare parents:
Banco:System.MarshalByRefObject;
}
Temos dois aspectos
apenas por questões de
implantação
Aspecto de distribuição,
cliente
Acrescenta atributos a
uma classe
aspect DistributionAspectClient {
declare class attributes:
(Conta || SaldoInsuficienteException)
[System.Serializable()];
...
Aspecto de distribuição
void 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
Quaisquer argumentos
pointcut localUpdate(Conta c):
this(CadContas) && target(c) &&
(call(any Conta.Creditar(..)) || ...;
private ArrayList dirtyObjects = ...;
after(Conta c) returning(): localUpdate(c) {
dirtyObjects.add(c);
}
Atualização dos objetos
modificados
pointcut 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
publics omitidos...
privileged aspect StateSynchronization
percflow(execution(any Banco.any(..))) {
RepositorioContas CadastroContas.Contas {
get {
return this.contas;
}
}
Para evitar interferências
no atributo do aspecto
Adiciona 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
Outros pointcut designators
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>
Mais 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 construções de Eos
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
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
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");
}...
Fonte: Eos distribution
Sincronização entre
conjuntos, aspecto
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;
}
Fonte: Eos distribution
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
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);...
}
Fonte: Eos distribution
Acessando mais informações
dos join points
Alé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 simples
public 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ão
public 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
Nome do método deixa de
contribuir para o pointcut
[Call(Invoke.Before,Alias="Say*")]
public void Say() {
System.Console.Write(“-> SayHello!");
}
LOOM.NET, mais invasão
class 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ência
public 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]