post sharp presentation
TRANSCRIPT
An Introduction to Aspect-Oriented Programming in Microsoft .NET.
Produce Cleaner Code with Aspect-Oriented Programming
Christophe [email protected]://www.cgeers.com/
AOP Facts
•AOP is 15 years oldMature
•Siemens, Hitachi, SAP, ASML•WebSphere, JBoss, WebLogic
Industry-Adopted
•-15% Lines of Code•-20% CouplingReal Benefits
Agenda• The Problem with Conventional Programming
• What is AOP?
• Why AOP?
• PostSharp Features
• Comparing AOP Frameworks
The Problem with Conventional Programming
Part 1
In the beginning there was nothing.
public class RentalService{}
public class RentalService{ public void RentMovie(Guid movieId, Guid customerId) { Movie movie = Movie.GetById(movieId); Customer customer = Customer.GetById(customerId); var history = new RentalHistory(); history.MovieId = movieId; history.CustomerId = customerId; history.RentedOn = DateTime.Now; customer.History.Add(history); customer.Balance -= movie.RentalPrice; }}
Customer said: let there be business value.
And there was business code.
Testers said: Let there be
logging
And there was logging code.
public class RentalService{ private static readonly TraceSource Trace = new TraceSource(typeof (RentalService).FullName); public void RentMovie(Guid movieId, Guid customerId) { Trace.TraceInformation( "Entering RentalService.RentMovie(movieId = {0},_customerId = {1})", movieId, customerId); try { Movie movie = Movie.GetById(movieId); Customer customer = Customer.GetById(customerId); var history = new RentalHistory(); history.MovieId = movieId; history.CustomerId = customerId; history.RentedOn = DateTime.Now; customer.History.Add(history); customer.Balance -= movie.RentalPrice; Trace.TraceInformation( "Leaving RentalService.RentMovie(movieId = {0}, customerId = {1})", movieId, customerId); } catch (Exception ex) { Trace.TraceEvent(TraceEventType.Error, 0, "Exception: RentalService.RentMovie(movieId = {0}, customerId = {1}) failed: {2}", movieId, customerId, ex.Message); throw; } }}
Thenthere was
precondition checking code.
public class RentalService{ private static readonly TraceSource Trace = new TraceSource(typeof (RentalService).FullName); public void RentMovie(Guid movieId, Guid customerId) { if (movieId == Guid.Empty) throw new ArgumentException("movieId"); if (customerId == Guid.Empty) throw new ArgumentException("customerId"); Trace.TraceInformation( "Entering RentalService.RentMovie(movieId = {0},_customerId = {1})", movieId, customerId); try { Movie movie = Movie.GetById(movieId); Customer customer = Customer.GetById(customerId); var history = new RentalHistory(); history.MovieId = movieId; history.CustomerId = customerId; history.RentedOn = DateTime.Now; customer.History.Add(history); customer.Balance -= movie.RentalPrice; Trace.TraceInformation( "Leaving RentalService.RentMovie(movieId = {0}, customerId = {1})", movieId, customerId); } catch (Exception ex) { Trace.TraceEvent(TraceEventType.Error, 0, "Exception: RentalService.RentMovie(movieId = {0}, customerId = {1}) failed : {2}", movieId, customerId, ex.Message); throw; } }}
Devs said: Let there be
defensive programming
public class RentalService{ private static readonly TraceSource Trace = new TraceSource(typeof (RentalService).FullName); public void RentMovie(Guid movieId, Guid customerId) { if (movieId == Guid.Empty) throw new ArgumentException("movieId"); if (customerId == Guid.Empty) throw new ArgumentException("customerId"); Trace.TraceInformation( "Entering RentalService.RentMovie(movieId = {0},_customerId = {1})“, movieId, customerId); try {
using (var scope = new TransactionScope()) { Movie movie = Movie.GetById(movieId); Customer customer = Customer.GetById(customerId); var history = new RentalHistory(); history.MovieId = movieId; history.CustomerId = customerId; history.RentedOn = DateTime.Now; customer.History.Add(history); customer.Balance -= movie.RentalPrice; ContextManager.Context.SaveChanges(); scope.Complete();
Trace.TraceInformation( "Leaving RentalService.RentMovie(movieId = {0}, customerId = {1})“, movieId, customerId); } } catch (Exception ex) { Trace.TraceEvent(TraceEventType.Error, 0, "Exception: RentalService.RentMovie(movieId = {0}, customerId = {1}) failed : {2}", movieId, customerId, ex.Message); throw; } }}
And there was transaction handling code.Let there be transactions.
public class RentalService{ private static readonly TraceSource Trace = new TraceSource(typeof (RentalService).FullName); public void RentMovie(Guid movieId, Guid customerId) { if (movieId == Guid.Empty) throw new ArgumentException("movieId"); if (customerId == Guid.Empty) throw new ArgumentException("customerId"); try { Trace.TraceInformation( "Entering RentalService.RentMovie(movieId = {0},_customerId = {1})“, movieId, customerId); try { using (var scope = new TransactionScope()) { Movie movie = Movie.GetById(movieId); Customer customer = Customer.GetById(customerId); var history = new RentalHistory(); history.MovieId = movieId; history.CustomerId = customerId; history.RentedOn = DateTime.Now; customer.History.Add(history); customer.Balance -= movie.RentalPrice; ContextManager.Context.SaveChanges(); scope.Complete(); Trace.TraceInformation( "Leaving RentalService.RentMovie(movieId = {0}, customerId = {1})“, movieId, customerId);
} } catch (Exception ex) { Trace.TraceEvent(TraceEventType.Error, 0, "Exception: RentalService.RentMovie(movieId = {0}, customerId = {1}) failed : {2}", movieId, customerId, ex.Message); throw; } } catch (Exception ex) { if (ExceptionManager.Handle(ex)) throw; } }}
And there was exception handling code.Let there be user-friendly error messages.
• We want a nice separation of concerns (assembly > namespace > class > method)
• OOP forces us to write crap!
• Code Scattering
• Code Tangling
• Code Coupling
Layer 1
Layer 2
Why do we write ugly code?
• Security
• Exception Handling
• Tracing
• Monitoring
• Transaction
• Data Binding
• Thread Sync
• Caching
• Validation
Non-Functional Requirements
Cross-Cutting Concerns
Encapsulating Infrastructure Concerns?
Aspects!
Strengthen Applications With Aspects
Show Me!
Show Me!1. Add a reference to PostSharp.dll
Show Me!2. Write an aspect {Demo}
Show Me!3. Apply the aspect {Demo}
Show Me!How does it work?
1. Source 2. Compiler 3. PostSharp 4. Run Time
The Idea Behind AOPPart 2
Cross-Cutting Concerns
Separation of Concerns
Problem Domain
Solution Domain
What is AOP?An extension of (not an alternative to) OOP that addresses the issue of cross-cutting concerns by providing a mean to:
• Encapsulate cross-cutting concerns
into Aspects = collection of transformations of code
• Apply aspects to elements of code
15 Years of AOP History
1994-1996
• First efforts on program transformation
1997
•AOP coined by Gregor Kiczales (Xerox PARC)
2001
•AspectJ published;• First AOSD
Conference
2003
•AspectJ released to the Eclise OSS community
• Spring Framework 1.0
• .NET 1.0
2004
•Build up of Interface21, later SpringSource, around IoC and AOP
•Works Begins on PostSharp
• JBoss AOP•WebSphere AOP•AJDT• SAP Enhancement
Framework
2007-2008
• PostSharp 1.0• PostSharp 1.5•ALCOB (AOP for
COBOL)
2009
• SpringSource acquired by VMWare, $400M
2010
• PostSharp 2.0
Research Years Hype Years Productivity Years
Why You Should Care
The benefits of aspect-oriented programming
The benefits of aspect-oriented programmingDecrease Development Costs• Write Fewer lines of code
• Read Fewer lines of code
• Concise, clear, understandable code
• Size-Cost relationship is superlinear
The benefits of aspect-oriented programmingImprove Quality• Fewer lines of code
→ Fewer Defects• More automation
→ Fewer Defects• Less boiler-plate code
→ More interesting work → Increased attention
→ Fewer Defects
The benefits of aspect-oriented programmingDecrease Maintenance Costs• Remember:
• Fewer Defects• Maintenance = 75% Reading Code
• How do you change a pattern once it’s implemented?
• Better architecture metrics:
• Decreased component coupling
• Increased component cohesion
The benefits of aspect-oriented programmingDecrease Maintenance Costs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 160
5
10
15
20
25
30
RedesignRedesign
Redesign
Redesign
Redesign
Conventional AOP Redesign
Time
Com
plex
ity
Features
FeaturesCode Transformation Primitives
• Around Methods
• Method Interception
• Property Interception
• Field Interception
• Event Interception
• Interface Introduction
• Method Introduction
• Property Introduction
• Event Introduction
• Member Import
• Custom Attribute Intro
• Managed Resource Intro
IntroductionsModifications
FeaturesComposite Aspects• Aspects composed of multiple primitive transformations
• Advice = Additional Behavior ≈ Transformation
• Pointcut = Expression selecting target elements of code
• Declarative
• LINQ over System.Reflection
• Adding aspects dynamically: IAspectProvider
FeaturesAspect MulticastingUsing a single line of code, apply an aspects to multiple elements of code based on:
• Attributes (public/private, virtual/sealed, …)
• Naming conventions
FeaturesAttribute Inheritance• Interfaces
• Classes
• Virtual Methods
• Assemblies (!)
Object
Entity
Customer
Order
Message
Update Message
Create Message
- or -
Robust Aspect Composition• Multiple aspects on the same element of code
• Aspect dependency framework
• Ordering
• Requirement
• Conflict
• Strong ordering and commutativity
Deterministic Behavior
D
CB
A
Comparing Aspect FrameworksPart 4
Comparing Aspect FrameworksStatic vs Dynamic AOP
Build-Time:Very ExpressiveRobust ModelNot InvasiveStatic
Run-Time:Less ExpressiveBrittle ModelInvasiveDynamic
Hybrid
PostSharp
Spring.NETCastleMS Unity/PIAB
LinFu
Comparing Aspect FrameworksExpressiveness (1/2)
PostSharp Linfu Spring.NET Castle Unity/PIAB
Method Interception Yes Yes Yes YesInterface Introduction Yes Yes Yes YesPrivate/Sealed Member Interception Yes Yes
Event Interception YesMember Introduction YesComposite Aspects Yes
What can you do with the framework?
Comparing Aspect FrameworksExpressiveness (2/2)
PostSharp Linfu Spring.NET Castle Unity/PIAB
Custom Attributes Yes Yes Yes YesCustom C# Code Yes Yes Yes FeasibleAspect Inheritance YesXML Config. Yes Yes
How do you apply aspects to code?
Comparing Aspect FrameworksNon-Invasiveness
PostSharp Linfu Spring.NET Castle Unity/PIAB
No change required in base source code Yes Yes
Use on any object (e.g. WPF control) Yes Yes
Can you use aspects without deep refactoring?
Require the use of factory methods
Comparing Aspect FrameworksRobustness
PostSharp Linfu Spring.NET Castle Unity/PIAB
Aspect Validation Yes FeasibleAspect Dependency Framework Yes
Can you prevent aspects from being improperly used?
Comparing Aspect FrameworksMisc.
PostSharp Linfu Spring.NET Castle Unity/PIAB
Dynamic: change aspects without recompiling
Yes Yes Yes
Productive: Visual Studio tooling Yes
No Impact on Build Time Yes Yes
Support: Extensive Documentation, Commercially Supported
Yes Yes Yes
Other points that matter
http://www.sharpcrafters.com/[email protected]