Date post: | 13-Jul-2015 |
Category: |
Software |
Upload: | vagif-abilov |
View: | 148 times |
Download: | 1 times |
Vagif Abilov
SOLID programming with
portable class libraries
About myself
• Mail: [email protected]
• Twitter: @ooobject
• GitHub: object
• BitBucket: object
• Blog: http://bloggingabout.net/blogs/vagif/default.aspx
• Some articles: http://www.codeproject.com
• Some open source projects:
– Simple.Data OData adapter
– Simple.OData.Client
– MongOData
– PCL Conformance Analyzer
Poll: do you care about PCL?
• Are you familiar with the concept of PCL?
• Have you used portable class libraries?
• Have you built your own portable class libraries?
• Do you maintain source code for applications that need
to be deployed on multiple platforms?
By the way, what is «portability»?
According to Wikipedia:
Portability in high-level computer programming is the
usability of the same software in different environments.
Strategies for portability
• Transferring installed program files to another computer of basically
the same architecture.
• Reinstalling a program from distribution files on another computer of
basically the same architecture.
• Building executable programs for different platforms from source
code; this is what is usually understood by "porting".
Portabilitity definition (cont’d)
Achieving portability between different processors,
according to Wikipedia:
• Non-web programs, installed upon a computer in the
normal manner, can have more control, and yet achieve
system portability by linking to the Java package.
• Software can be recompiled and linked from source
code for different operating systems and processors if
written in a programming language supporting
compilation for the platforms.
Innovative portability strategies
• Xamarin products: compiling to native apps
– Binding Objective-C libraries (iOS)
– Binding Java libraries (Android)
• Portable class libraries: managed assemblies that work
on more than one .NET Framework platform
– .NET 4.0, 4.0.3, 4.5
– Silverlight 4, 5
– Windows Phone 7, 7.5, 8
– .NET for Windows Store applications
– Xbox 360
Recompilation vs. binary reuse
Does it really matter if we package code in
reusable assemblies? Can we compile our
code for a new target platform when we
actually need it?
PCL advantages
• If the code is not bound to a specific platform, then
packaging it in a portable class library will guard it from
unintended platform dependencies by enforcing
portability constraints at early development stage
• Packaging code as PCL may require introduction of
higher level abstractions, extraction of interfaces,
inversion of dependencies and provide guidance to
follow SOLID principles
Platform support in PCL
Authoring portable class libraries
Case study
From
Simple.Data OData adapter
to
Simple.OData.Client PCL
What is Simple.Data
• A lightweight, dynamic data access component for
.NET
• Written and maintained by Mark Rendle
• Adapters for SQL Server, Oracle, Sqlite, MongoDB,
OData, Oracle, PostgreSql, Informix
• An alternative to ORM libraries, such as Entity
Framework and NHibernate
Simple.Data code example
var db = Database.Open();
var titles = db.Albums
.All()
.Select(db.Albums.Title)
.Where(db.Albums.GenreId == 1 &&
db.Albums.AlbumId > 400);
Simple.Data OData adapter
• An alternative to WCF DataServices client
• Better fits RESTful nature of OData protocol than SOAP
alike client code generation triggered with «Add Service
Reference»
Simple.Data.OData code example
var db = Database.Opener.Open(
"http://packages.nuget.org/v1/FeedService.svc/");
var package1 = db.Packages
.FindByTitle("Simple.Data.OData");
var package2 = db.Packages
.Find(db.Packages.Title == "Simple.OData.Client");
Generate HTTP GET request URLs:
Packages?$filter=Title+eq+%27Simple.Data.OData%27
Packages?$filter=Title+eq+%27Simple.OData.Client%27
Adapter
• Structural design pattern
• Converts the interface of a class into another interface
clients expect
Common API
Adapter
External service
Simple.Data OData version <= 0.5
Simple.Data API
Simple.Data OData Adapter
OData protocol
ODataPad
Making the adapter portable
• An adapter can target new platforms as long as it
provides a bridge between interfaces (and interfaces
don’t refer to types bound to specific platforms)
• OData protocol is platform agnostic
• Most of OData adapter code deals with either parsing
XML documents returned by an OData service or
formatting CLR objects as XML documents to send to
OData service
• Simple.Data API uses types defined in Simple.Data
library
• Simple.Data supports Mono (hope of portability with
other platforms)
Targeting Windows Store apps
System.Data namespace
• Microsoft.SqlServer.Server
• System.Configuration
• System.Data
• System.Data.Common
• System.Data.Odbc
• System.Data.OleDb
• System.Data.Sql
• System.Data.SqlClient
• System.Data.SqlTypes
• System.Xml
PCL Conformance Analyzer
Demo
Simple.Data.OData version >= 0.6
Simple.Data API
Simple.Data OData Adapter
Simple.OData.Client PCL
OData protocol
Simple.OData.Client
• Version 0.13
– .NET 4.0, .NET 4.0.3, 4.5
– Windows Store
– Silverlight 5
– Windows Phone 8
• Version 0.17
– Xamarin.Android
– Xamarin.iOS
But what about SOLID principles?
Example
Adding support for authentication
User request: support authentication
• First implementation: accept user credentials (user +
password), create authentication object using one of
supported schemes (Basic, Windows etc.)
• Worked like a charm, easy to use in client code
var odataFeed = new ODataFeed(
"http://www.myservice.com/api",
"Vagif",
"Password123");
• At that time Simple.Data OData adapter included non-
portable version of Simple.OData.Client
ODataFeed
public class ODataFeed
{
public string Url { get; set; }
public string User { get; set; }
public string Password { get; set; }
public string Domain { get; set; }
public bool IntegratedSecurity { get; set; }
}
Creating Web request
var request = (HttpWebRequest)WebRequest.Create(uri);
if (this.Credentials.IntegratedSecurity)
{
request.Credentials = CredentialCache.DefaultNetworkCredentials;
}
else if (!string.IsNullOrEmpty(this.Credentials.User))
{
request.Credentials = new NetworkCredential(
this.Credentials.User,
this.Credentials.Password,
this.Credentials.Domain);
}
Merging with Portable branch
Project doesn’t compile!
• System.Net.CredentialCache: .NET 4.x only
• System.Net.NetworkCredential: most of platforms
What went wrong?
• Simple.Data OData adapter took responsibility to create
user credentials based on sensitive user information
• Leaving aside security aspects, the adapter violated
single responsibility principle
• The adapter restricted supported authentication metods
to those provided by credential creation code
• The adapter is not open to extending it with new
authentication methods, so it violated open/closed
principle too
• Use of interface segregation principle would avoid this
mistake
• PCL compliance forced use of interfaces
Revised implementation
In platform-spefic client code
var odataFeed = new ODataFeed(
"http://www.myservice.com/api",
credentials);
In Simple.OData.Client PCL
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Credentials = this.Credentials;
Revised implementation
• Credentials is an instance of a class that implements
System.Net.ICredentials interface
• Neither Simple.Data OData adapter (.NET 4.x) nor
Simple.OData.Client refer to a specific authentication
scheme
• All present and future authentication schemes are
supported as long as they conform ICredentials
Observations
• Some SOLID principles require coding discipline and
leave a room for interpretation, IMHO especially SRP
and OCP (John Skeet on OCP: «While I've obviously considered the
possibility that I'm the only one who finds it confusing, I've heard enough variation in
the explanations of it to suggest that I'm really not the only one»)
• PCL conformance requirement doesn’t release you
from the responsibility to make the design decision, but
it can guard you from making obvious mistakes and
sometimes even guide you in a right direction
• PCLs make you more carefully plan service
instantiation and use of non-functional utilities (logging,
instrumentation etc.)
PCLs and concrete classes
• Portable class libraries do not push the work of
implementing platform-specific services to client
applications
• PCLs can be packaged as a single portable deployment
unit
– Autofac
– Json.NET
– Simple.OData.Client
• PCLs can also be compound, consisting of core
portable and platform-specific parts
– MetroLog
– Splat
MetroLog architecture
MetroLog NuGet specification
<files>
<file src="MetroLog.dll" target="lib\portable-
net45+wp8+win8\MetroLog.dll" />
<file src="MetroLog.dll" target="lib\net45\MetroLog.dll" />
<file src="MetroLog.NetFx.dll" target="lib\net45\MetroLog.NetFx.dll" />
<file src="MetroLog.dll" target="lib\netcore45\MetroLog.dll" />
<file src="MetroLog.NetCore.dll"
target="lib\netcore45\MetroLog.NetCore.dll" />
</files>
PCLs consuming PCLs
• A PCL client can also be a portable library
• Client target platforms must be a subset of the
referenced PCL’s target platforms
• Functionality that requires platform-specific services is
usually referred using interfaces and abstract classes
• There is a trick to use concrete platform-specific
classes in client PCLs by placing in the referenced PCL
a dummy class with the same API surface and
assembly identity as the platform-specific class
PCL profiles and portable subsets
• Profile is a set of supported platforms
• Portable subset is a family of profiles that expose
certain version of .NET FX API surface area
– Profile 78: Portable Subset:
• .NET 4.5
• Windows Phone 8
• Windows Store
– Profile 95: Portable Subset (Legacy):
• .NET 4.0.3 and higher
• Silverlight 4 and higher
• Windows Phone 7 and higher
• Windows Store
PCLs for Android and iOS
Demo: Xamarin .NET Mobility Scanner
Example: Reflection API portability
Polyglot programming with PCLs
• Use right language to solve specific problems
• C# provides the best ‘one size fits all’ choice
• F# is very efficient for immutable data transformations,
financial computations, machine learning
• F# code can be packaged in a PCL and shared among
different platforms (inluding Android and iOS!)
– No official support to target Windows Phone 8 using F# PCL, but there
is a workaround
– Both PCL and F# support in Xamarin are work in progress (with
changes being made literally while I am speaking now)
• Core logic can be written in C# and F# and packaged
as PCL, and UI is added using platform-specific tools
PCLs for the future
• Profiles for v.4.0 API surface are being deprecated
• Visual Studio 2013 can open PCLs that target legacy
platforms, but it will upgrade Silverlight to target version
5 and Windows Phone to target version 8
• Xamarin PCLs targets both v.4.0 and v.4.5 API surfaces
• If a library target wide range of platforms (both 4.0 and
4.5), its NuGet package should include separate binaries
for each surface
• Consider only targeting v.4.5 API surface for new
projects unless you need to support legacy platforms
Using PCLs in UI
• Use of portable class libraries can result in significant
code reuse in cross-platform application development
• Most popular approach to cross-platform UI with PCLs
is to use MVVM pattern and package core services,
models and view models in a portable library
• Most popular MVVM frameworks that have PCLs are
MvvmLights and MvvmCross
• MvvmCross supports targeting Xamarin.iOS and
Xamarin.Android (and provides phenomenal support at
StackOverflow by @slodge)
Example: Lions Roar
• Developed by Sequence Agency
• UI is built using MvvmCross
• View models PCL (2463 LOC)
• Entities PCL (691 LOC)
• Supported plalforms
– Windows Store (1166 LOC)
– Windows Phone 8 (668)
– Android Phone/Tablet (1172)
– iPhone/iPad (2000 LOC)
Using PCL in ODataPad UI
• ODataPad views show images
• Original view model design included core portable base
view model (without image data) and platform-specific
view models (with image data)
• Small picture size makes possible storing images in
base64 format and reuse a single view model in all
platforms
• Rendering images requires platform-specific value
converters
• A PCL with a design-time view model serves design
data to all Visual Studio designers (Blend)
Conclusion
• Portable class libraries are not only for binary reuse
• Packaging code as PCLs helps making code cleaner:
– Extract interfaces
– Unify platform-specific services
– Inject service dependencies
– Use portable data structures
• Consider PCLs when choosing third party libraries
– Ready for other platforms
– Indication of a proper design
– May only have dependencies to other portable libraries
• Consider make your next library portable even if you
only target a single platform!
Resources
• Daniel Plaisted «How to Make Portable Class Libraries
Work for You»
• Scott Hanselman «Cross-Platform Portable Class
Libraries with .NET are Happening»
Open source projects at GitHub:
• AutoFac
• MetroLog
• Splat
• MvvmCross
• Simple.OData.Client
Thank you!
• Mail: [email protected]
• Twitter: @ooobject
• GitHub: object
• BitBucket: object
• Blog: http://bloggingabout.net/blogs/vagif/default.aspx
The source code for this PCL Conformance Analyzer can
be found at https://github.com/object/PclAnalyzer