xamarin
Condivisione del codice tra i progetti
Ricerca…
Il modello del ponte
Il pattern Bridge è uno degli schemi di progettazione di Inversion of Control più basilari. Per Xamarin, questo modello viene utilizzato per fare riferimento al codice dipendente dalla piattaforma da un contesto indipendente dalla piattaforma. Ad esempio: utilizzando AlertDialog di Android da una libreria di classi portatile o moduli Xamarin. Nessuno di quei contesti sa cosa sia un oggetto AlertDialog, quindi è necessario inserirlo in una casella affinché possano essere utilizzati.
// Define a common interface for the behavior you want in your common project (Forms/Other PCL)
public interface IPlatformReporter
{
string GetPlatform();
}
// In Android/iOS/Win implement the interface on a class
public class DroidReporter : IPlatformReporter
{
public string GetPlatform()
{
return "Android";
}
}
public class IosReporter : IPlatformReporter
{
public string GetPlatform()
{
return "iOS";
}
}
// In your common project (Forms/Other PCL), create a common class to wrap the native implementations
public class PlatformReporter : IPlatformReporter
{
// A function to get your native implemenation
public static func<IPlatformReporter> GetReporter;
// Your native implementation
private IPlatformReporter _reporter;
// Constructor accepts native class and stores it
public PlatformReporter(IPlatformReporter reporter)
{
_reporter = GetReporter();
}
// Implement interface behavior by deferring to native class
public string GetPlatform()
{
return _reporter.GetPlatform();
}
}
// In your native code (probably MainActivity/AppDelegate), you just supply a function that returns your native implementation
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.activity_main);
PlatformReporter.GetReporter = () => { return new DroidReporter(); };
}
}
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
window.RootViewController = new UIViewController();
window.MakeKeyAndVisible();
PlatformReporter.GetReporter = () => { return new IosReporter(); };
return true;
}
}
// When you want to use your native implementation in your common code, just do as follows:
public void SomeFuncWhoCares()
{
// Some code here...
var reporter = new PlatformReporter();
string platform = reporter.GetPlatform();
// Some more code here...
}
Il modello di localizzazione del servizio
Il modello di progettazione di Service Locator è un'iniezione molto vicina alla dipendenza. Come il Bridge Pattern, questo modello può essere utilizzato per fare riferimento al codice dipendente dalla piattaforma da un contesto indipendente dalla piattaforma. Più interessante, questo modello si basa sul modello singleton: tutto ciò che inserisci nel localizzatore di servizio sarà un defacto singleton.
// Define a service locator class in your common project
public class ServiceLocator {
// A dictionary to map common interfaces to native implementations
private Dictionary<object, object> _services;
// A static instance of our locator (this guy is a singleton)
private static ServiceLocator _instance;
// A private constructor to enforce the singleton
private ServiceLocator() {
_services = new Dictionary<object, object>();
}
// A Singleton access method
public static ServiceLocator GetInstance() {
if(_instance == null) {
_instance = new ServiceLocator();
}
return _instance;
}
// A method for native projects to register their native implementations against the common interfaces
public static void Register(object type, object implementation) {
_services?.Add(type, implementation);
}
// A method to get the implementation for a given interface
public static T Resolve<T>() {
try {
return (T) _services[typeof(T)];
} catch {
throw new ApplicationException($"Failed to resolve type: {typeof(T).FullName}");
}
}
//For each native implementation, you must create an interface, and the native classes implementing that interface
public interface IA {
int DoAThing();
}
public interface IB {
bool IsMagnificent();
}
public class IosA : IA {
public int DoAThing() {
return 5;
}
}
public class DroidA : IA {
public int DoAThing() {
return 42;
}
}
// You get the idea...
// Then in your native initialization, you have to register your classes to their interfaces like so:
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.activity_main);
var locator = ServiceLocator.GetInstance();
locator.Register(typeof(IA), new DroidA());
locator.Register(typeof(IB), new DroidB());
}
}
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
window.RootViewController = new UIViewController();
window.MakeKeyAndVisible();
var locator = ServiceLocator.GetInstance();
locator.Register(typeof(IA), new IosA());
locator.Register(typeof(IB), new IosB());
return true;
}
}
// Finally, to use your native implementations from non-native code, do as follows:
public void SomeMethodUsingNativeCodeFromNonNativeContext() {
// Some boring code here
// Grabbing our native implementations for the current platform
var locator = ServiceLocator.GetInstance();
IA myIA = locator.Resolve<IA>();
IB myIB = locator.Resolve<IB>();
// Method goes on to use our fancy native classes
}