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]
Download

public class Conta