SlideShare a Scribd company logo
1 of 30
可抽換元件設計模式
相依
 名詞:dependency
 動詞:depend(s) on
 形容詞:dependent on
類別UserAppService相依於
UserRepository及Logger兩個類
別
public class UserAppService
{
public void AddUser(UserInfo user)
{
UserRepository repository = new UserRepository();
repository.Add(user);
Logger log = new Logger();
Dictionary<string, string> messages = new Dictionary<string, string>();
messages.Add("Action", "建立使用者");
messages.Add("Detail", string.Format("UserId: {0}, UserName: {1}", user.UserId,
user.UserName));
messages.Add("LogTime", DateTime.Now.ToString());
log.Write(messages);
}
}
耦合
 Coupling,以程度區分為
loose-coupling / loosely-coupled / 鬆散耦合:
相依於介面(interface)
tight-coupling / tightly-coupled / 緊密耦合:
相依於實作(implementation)
需求案例
驚!某甲要求導入log機制,且不得使用third-
party元件(如log4net, NLog, Enterprise library
的Logging Application Block, etc.)
一塊蛋糕
public class TextLogger
{
public void Write(Dictionary<string, string> messages)
{
StringBuilder sb = new StringBuilder();
messages.ToList().ForEach(c => sb.AppendLine(string.Format("{0}:{1}", c.Key, c.Value)));
sb.AppendLine();
File.AppendAllText(string.Format("{0}.txt", DateTime.Now.ToString("yyyyMMdd")), sb.ToString());
}
}
public class UserAppService
{
public void AddUser(UserInfo user)
{
UserRepository repository = new UserRepository();
repository.Add(user);
TextLogger log = new TextLogger();
Dictionary<string, string> messages = new Dictionary<string, string>();
messages.Add("Action", "建立使用者");
messages.Add("Detail", string.Format("UserId: {0}, UserName: {1}", user.UserId, user.UserName));
messages.Add("LogTime", DateTime.Now.ToString());
log.Write(messages);
}
}
WHAT IF…
 某甲:文字檔太粗糙,請log成XML格式的檔
案
 某乙:沒問題!
仍然是一塊蛋糕
public class XmlLogger
{
public void Write(Dictionary<string, string> messages)
{
XElement element = new XElement(
new XElement("Record",
new XElement("Action", messages["Action"]),
new XElement("Detail", messages["Detail"]),
new XElement("LogTime", messages["LogTime"])
)
);
File.AppendAllText(string.Format("{0}_{1}.xml", DateTime.Now.ToString("yyyyMMdd"), Guid.NewGuid()),
element.ToString());
}
}
public class UserAppService
{
public void AddUser(UserInfo user)
{
…
XmlLogger log = new XmlLogger();
…
}
}
WHAT IF AGAIN…
 某甲:長官說要改成寫進資料庫唷, ^.<
 某乙:…(OS: what the…(╯-_-)╯╧╧ )
翻桌前思考一下
 耦 合 度 太 高 。 系 統 提 供 新 的 log 機 制 時 ,
UserAppService類別就必須修改實作內容以
符合需求(TextLogger->XmlLogger)
 彈性不足,無法任意抽換實作機制
解決方案
 Plugin Pattern
 Provider Pattern (.NET Framework內建)
 IoC Pattern
PLUGIN PATTERN
 Links classes during configuration rather
than compilation. – Patterns of Enterprise
Application Architecture [P of EAA], p.499
透過反射(reflection)機制於執行期(run time)
由設定檔(configuration)取得實際要執行的物
件 TextLogger / XmlLogger
/ …
PLUGIN PATTERN特色
 會有一個實作Factory Method Pattern的類別,
用於生成實際要執行之物件(plugin object)
 設定檔內會有實際生成物件的組件名稱及型
別,如放置在App.config或Web.config的
appSettings中
 實際生成之物件必定實作一通用介面
PLUGIN PATTERN實作(1)
 建立一通用介面
public interface ILogger
{
void Write(Dictionary<string, string> messages);
}
PLUGIN PATTERN實作(2)
 建立TextLogger類別(plugin object),並實作
ILogger介面
public class TextLogger : ILogger
{
public void Write(Dictionary<string, string> messages)
{
StringBuilder sb = new StringBuilder();
messages.ToList().ForEach(c => sb.AppendLine(string.Format("{0}:{1}", c.Key, c.Value)));
sb.AppendLine();
File.AppendAllText(string.Format("{0}.txt", DateTime.Now.ToString("yyyyMMdd")),
sb.ToString());
}
}
PLUGIN PATTERN實作(3)
 建立XmlLogger類別(plugin object) ,並實作
ILogger介面
public class XmlLogger : ILogger
{
public void Write(Dictionary<string, string> messages)
{
XElement element = new XElement(
new XElement("Record",
new XElement("Action", messages["Action"]),
new XElement("Detail", messages["Detail"]),
new XElement("LogTime", messages["LogTime"])
)
);
File.AppendAllText(string.Format("{0}_{1}.xml", DateTime.Now.ToString("yyyyMMdd"),
Guid.NewGuid()), element.ToString());
}
}
PLUGIN PATTERN實作(4)
 建立一實作Factory Method Pattern的類別
public class LoggerFactory
{
private static ILogger _logger;
public static ILogger CreateLogger()
{
if (_logger == null)
{
string assemblyName = ConfigurationManager.AppSettings["AssemblyName"];
string classType = ConfigurationManager.AppSettings["ClassType"];
Assembly assembly = Assembly.Load(assemblyName);
_logger = assembly.CreateInstance(classType) as ILogger;
}
return _logger;
}
}
 建立設定檔
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="AssemblyName" value="LoggerSample"/>
<add key="ClassType" value="LoggerSample.PluginPattern.TextLogger"/>
</appSettings>
</configuration>
PLUGIN PATTERN實作(5)
Fully qualified name
 修改相依TextLogger/XmlLogger/…類別的程
式碼
public class UserAppService
{
public void AddUser(UserInfo user)
{
UserRepository repository = new UserRepository();
repository.Add(user);
//TextLogger log = new TextLogger();
ILogger log = LoggerFactory.CreateLogger();
Dictionary<string, string> messages = new Dictionary<string, string>();
messages.Add("Action", "建立使用者");
messages.Add("Detail", string.Format("UserId: {0}, UserName: {1}", user.UserId,
user.UserName));
messages.Add("LogTime", DateTime.Now.ToString());
log.Write(messages);
}
}
PLUGIN PATTERN實作(6)
相依實作轉為相依介面,耦合度降低
 若要切換log機制,僅須修改設定檔
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="AssemblyName" value="LoggerSample"/>
<add key="ClassType" value="LoggerSample.PluginPattern.XmlLogger"/>
</appSettings>
</configuration>
PLUGIN PATTERN實作(7)
PLUGIN PATTERN兩三事
 一組介面,提供多種實作
 通用介面(ILogger)與實際要生成的物件(plugin
object)通常會存在於不同的組件中
 提供新的plugin object時,不需修改原始程式
碼(UserAppService),僅需提供新的plugin組
件並修改設定檔
 plugin object實作內容若有異動,僅需patch其
組件
PROVIDER PATTERN
 .NET Framework內建的設計模式
 提供一組介面,多種實作切換的功能
PROVIDER PATTERN特色
 會有一個實作Factory Method Pattern的類別,
用於生成實際要執行之物件
 會有一個繼承ConfigurationSection類別的類
別且設定檔內會存放可於執行期生成之物件
的組件名稱及型別
 實際生成之物件必定繼承ProviderBase類別
並實作一通用介面
PROVIDER PATTERN實作(1)
 建立一通用介面
public interface ILoggerProvider
{
void Write(Dictionary<string, string> messages);
}
PROVIDER PATTERN實作(2)
 建 立 TextLoggerProvider 類 別 , 繼 承
ProviderBase類別並實作ILoggerProvider介
面public class TextLoggerProvider : ProviderBase, ILoggerProvider
{
public void Write(Dictionary<string, string> messages)
{
StringBuilder sb = new StringBuilder();
messages.ToList().ForEach(c => sb.AppendLine(string.Format("{0}:{1}", c.Key, c.Value)));
sb.AppendLine();
File.AppendAllText(string.Format("{0}.txt", DateTime.Now.ToString("yyyyMMdd")),
sb.ToString());
}
}
PROVIDER PATTERN實作(3)
 建 立 XmlLoggerProvider 類 別 , 繼 承
ProviderBase類別並實作ILoggerProvider介
面public class XmlLoggerProvider : ProviderBase, ILoggerProvider
{
public void Write(Dictionary<string, string> messages)
{
XElement element = new XElement(
new XElement("Record",
new XElement("Action", messages["Action"]),
new XElement("Detail", messages["Detail"]),
new XElement("LogTime", messages["LogTime"])
)
);
File.AppendAllText(string.Format("{0}_{1}.xml", DateTime.Now.ToString("yyyyMMdd"),
Guid.NewGuid()), element.ToString());
}
}
PROVIDER PATTERN實作(4)
 建 立 LoggerProviderSection 類 別 並 繼 承
ConfigurationSection類別
public class LoggerProviderSection : ConfigurationSection
{
public const string SectionName = "loggerProvider";
[ConfigurationProperty("providers", IsDefaultCollection = true)]
public ProviderSettingsCollection Providers
{
get
{
return (ProviderSettingsCollection)base["providers"];
}
}
[ConfigurationProperty("defaultProvider")]
public string DefaultProvider
{
get
{
return (string)base["defaultProvider"];
}
set
{
base["defaultProvider"] = value;
}
}
}
PROVIDER PATTERN實作(5)
 建立一實作Factory Method Pattern的類別
public class LoggerProviderFactory
{
private static ProviderBase _provider;
public static ILoggerProvider CreateLoggerProvider()
{
if (_provider == null)
{
LoggerProviderSection section =
(LoggerProviderSection)ConfigurationManager.GetSection(LoggerProviderSection.SectionName);
ProviderSettings settings = section.Providers[section.DefaultProvider];
_provider = Activator.CreateInstance(Type.GetType(settings.Type)) as ProviderBase;
_provider.Initialize(settings.Name, settings.Parameters);
}
return _provider as ILoggerProvider;
}
}
 建立設定檔,設定預設
provider(defaultProvider)<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="loggerProvider" type="LoggerSample.ProviderPattern.LoggerProviderSection,
LoggerSample" />
</configSections>
<loggerProvider defaultProvider="xml">
<providers>
<add name="text" type="LoggerSample.ProviderPattern.TextLoggerProvider, LoggerSample" />
<add name="xml" type="LoggerSample.ProviderPattern.XmlLoggerProvider, LoggerSample" />
</providers>
</loggerProvider>
</configuration>
PROVIDER PATTERN實作(6)
Fully qualified name Assembly name
IOC PATTERN
 To be continued…
參考
 Patterns of Enterprise Application Architecture
 Microsoft .NET: Architecting Applications for the Enterprise
 Design Patterns: Elements of Reusable Object-Oriented Software
 Provider Pattern
 撰寫自己的 Configuration 區段 Part 1:不要再賴在 appSettings 的屋簷
下了,寫個自己的 Configuration 區段吧~
 Provider Model Design Pattern and Specification, Part 1
 ProviderBase Class
 Designing loosely coupled components in .NET - Provider Pattern

More Related Content

What's hot

Windows 8 Training Fundamental - 1
Windows 8 Training Fundamental - 1Windows 8 Training Fundamental - 1
Windows 8 Training Fundamental - 1
Kevin Octavian
 
java API for XML DOM
java API for XML DOMjava API for XML DOM
java API for XML DOM
Surinder Kaur
 
DOT NET LAB PROGRAM PERIYAR UNIVERSITY
DOT NET LAB PROGRAM PERIYAR UNIVERSITY DOT NET LAB PROGRAM PERIYAR UNIVERSITY
DOT NET LAB PROGRAM PERIYAR UNIVERSITY
GOKUL SREE
 
Introduction to dart - So@t - 20130410
Introduction to dart - So@t - 20130410Introduction to dart - So@t - 20130410
Introduction to dart - So@t - 20130410
yohanbeschi
 

What's hot (18)

4Developers: Dominik Przybysz- Message Brokers
4Developers: Dominik Przybysz- Message Brokers4Developers: Dominik Przybysz- Message Brokers
4Developers: Dominik Przybysz- Message Brokers
 
Android - Saving data
Android - Saving dataAndroid - Saving data
Android - Saving data
 
4Developers: Michał Szczepanik- Kotlin - Let’s ketchup it
4Developers: Michał Szczepanik- Kotlin - Let’s ketchup it4Developers: Michał Szczepanik- Kotlin - Let’s ketchup it
4Developers: Michał Szczepanik- Kotlin - Let’s ketchup it
 
Presentation of sexy.rgtk
Presentation of sexy.rgtkPresentation of sexy.rgtk
Presentation of sexy.rgtk
 
Backendless apps
Backendless appsBackendless apps
Backendless apps
 
Windows 8 Training Fundamental - 1
Windows 8 Training Fundamental - 1Windows 8 Training Fundamental - 1
Windows 8 Training Fundamental - 1
 
mediator
mediatormediator
mediator
 
Taming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, MacoscopeTaming Core Data by Arek Holko, Macoscope
Taming Core Data by Arek Holko, Macoscope
 
java API for XML DOM
java API for XML DOMjava API for XML DOM
java API for XML DOM
 
Mvc acchitecture
Mvc acchitectureMvc acchitecture
Mvc acchitecture
 
Url programming
Url programmingUrl programming
Url programming
 
mediator
mediatormediator
mediator
 
Metaworks3
Metaworks3Metaworks3
Metaworks3
 
Chat Room System using Java Swing
Chat Room System using Java SwingChat Room System using Java Swing
Chat Room System using Java Swing
 
DOT NET LAB PROGRAM PERIYAR UNIVERSITY
DOT NET LAB PROGRAM PERIYAR UNIVERSITY DOT NET LAB PROGRAM PERIYAR UNIVERSITY
DOT NET LAB PROGRAM PERIYAR UNIVERSITY
 
Introduction to dart - So@t - 20130410
Introduction to dart - So@t - 20130410Introduction to dart - So@t - 20130410
Introduction to dart - So@t - 20130410
 
안드로이드 데이터 바인딩
안드로이드 데이터 바인딩안드로이드 데이터 바인딩
안드로이드 데이터 바인딩
 
The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84The Ring programming language version 1.2 book - Part 5 of 84
The Ring programming language version 1.2 book - Part 5 of 84
 

Viewers also liked

Vicente Quirarte: poesía y Academia en México antes y después del nuevo milenio
Vicente Quirarte: poesía y Academia en México antes y después del nuevo milenioVicente Quirarte: poesía y Academia en México antes y después del nuevo milenio
Vicente Quirarte: poesía y Academia en México antes y después del nuevo milenio
IGNACIO BALLESTER PARDO
 
El Archivo Negro de la Poesía Mexicana: acerca del margen
El Archivo Negro de la Poesía Mexicana: acerca del margenEl Archivo Negro de la Poesía Mexicana: acerca del margen
El Archivo Negro de la Poesía Mexicana: acerca del margen
IGNACIO BALLESTER PARDO
 
Untitled Presentation
Untitled PresentationUntitled Presentation
Untitled Presentation
europas1231
 
Untitled Presentation
Untitled PresentationUntitled Presentation
Untitled Presentation
europas1231
 
Memperbaiki monitor lcd yang mati
Memperbaiki monitor lcd yang matiMemperbaiki monitor lcd yang mati
Memperbaiki monitor lcd yang mati
Bah Arzyl
 

Viewers also liked (17)

Vicente Quirarte: poesía y Academia en México antes y después del nuevo milenio
Vicente Quirarte: poesía y Academia en México antes y después del nuevo milenioVicente Quirarte: poesía y Academia en México antes y después del nuevo milenio
Vicente Quirarte: poesía y Academia en México antes y después del nuevo milenio
 
a.vilinchuk
a.vilinchuka.vilinchuk
a.vilinchuk
 
El Archivo Negro de la Poesía Mexicana: acerca del margen
El Archivo Negro de la Poesía Mexicana: acerca del margenEl Archivo Negro de la Poesía Mexicana: acerca del margen
El Archivo Negro de la Poesía Mexicana: acerca del margen
 
Untitled Presentation
Untitled PresentationUntitled Presentation
Untitled Presentation
 
La escritura del_dolor_vicente_quirarte
La escritura del_dolor_vicente_quirarteLa escritura del_dolor_vicente_quirarte
La escritura del_dolor_vicente_quirarte
 
Web Automation Testing Using Selenium
Web Automation Testing Using SeleniumWeb Automation Testing Using Selenium
Web Automation Testing Using Selenium
 
Pancasila
PancasilaPancasila
Pancasila
 
Ciudad de mexico_en_jose_emilio_pacheco_y_homero_aridjis_la_poesia_ecologica
Ciudad de mexico_en_jose_emilio_pacheco_y_homero_aridjis_la_poesia_ecologicaCiudad de mexico_en_jose_emilio_pacheco_y_homero_aridjis_la_poesia_ecologica
Ciudad de mexico_en_jose_emilio_pacheco_y_homero_aridjis_la_poesia_ecologica
 
Tik kelas IX bab 1
Tik kelas IX bab 1Tik kelas IX bab 1
Tik kelas IX bab 1
 
the better vacations
the better vacationsthe better vacations
the better vacations
 
Hst & metallography
Hst & metallographyHst & metallography
Hst & metallography
 
Untitled Presentation
Untitled PresentationUntitled Presentation
Untitled Presentation
 
Memperbaiki monitor lcd yang mati
Memperbaiki monitor lcd yang matiMemperbaiki monitor lcd yang mati
Memperbaiki monitor lcd yang mati
 
Project management
Project management  Project management
Project management
 
Kinematika dan Dinamika - Kerja dan Energi
Kinematika dan Dinamika - Kerja dan EnergiKinematika dan Dinamika - Kerja dan Energi
Kinematika dan Dinamika - Kerja dan Energi
 
Kepala perpustakaan
Kepala perpustakaanKepala perpustakaan
Kepala perpustakaan
 
Presentación Tertulia Literaria: entre ayer y hoy (II)
Presentación Tertulia Literaria: entre ayer y hoy (II)Presentación Tertulia Literaria: entre ayer y hoy (II)
Presentación Tertulia Literaria: entre ayer y hoy (II)
 

Similar to 可抽換元件設計模式

User controls
User controlsUser controls
User controls
aspnet123
 
JavaScript Lessons 2023 V2
JavaScript Lessons 2023 V2JavaScript Lessons 2023 V2
JavaScript Lessons 2023 V2
Laurence Svekis ✔
 
FileWrite.javaFileWrite.java  To change this license header.docx
FileWrite.javaFileWrite.java  To change this license header.docxFileWrite.javaFileWrite.java  To change this license header.docx
FileWrite.javaFileWrite.java  To change this license header.docx
ssuser454af01
 
Jug Guice Presentation
Jug Guice PresentationJug Guice Presentation
Jug Guice Presentation
Dmitry Buzdin
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
Alexey Buzdin
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
C.T.Co
 

Similar to 可抽換元件設計模式 (20)

Android dev toolbox
Android dev toolboxAndroid dev toolbox
Android dev toolbox
 
User controls
User controlsUser controls
User controls
 
JavaScript Lessons 2023 V2
JavaScript Lessons 2023 V2JavaScript Lessons 2023 V2
JavaScript Lessons 2023 V2
 
Academy PRO: ASP .NET Core
Academy PRO: ASP .NET Core Academy PRO: ASP .NET Core
Academy PRO: ASP .NET Core
 
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdfCreating a Facebook Clone - Part XXVIII - Transcript.pdf
Creating a Facebook Clone - Part XXVIII - Transcript.pdf
 
Do you know what your drupal is doing? Observe it!
Do you know what your drupal is doing? Observe it!Do you know what your drupal is doing? Observe it!
Do you know what your drupal is doing? Observe it!
 
FileWrite.javaFileWrite.java  To change this license header.docx
FileWrite.javaFileWrite.java  To change this license header.docxFileWrite.javaFileWrite.java  To change this license header.docx
FileWrite.javaFileWrite.java  To change this license header.docx
 
DataFX - JavaOne 2013
DataFX - JavaOne 2013DataFX - JavaOne 2013
DataFX - JavaOne 2013
 
Jug Guice Presentation
Jug Guice PresentationJug Guice Presentation
Jug Guice Presentation
 
Android getting started
Android getting startedAndroid getting started
Android getting started
 
droidQuery: The Android port of jQuery
droidQuery: The Android port of jQuerydroidQuery: The Android port of jQuery
droidQuery: The Android port of jQuery
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
 
DataFX 8 (JavaOne 2014)
DataFX 8 (JavaOne 2014)DataFX 8 (JavaOne 2014)
DataFX 8 (JavaOne 2014)
 
Fun Teaching MongoDB New Tricks
Fun Teaching MongoDB New TricksFun Teaching MongoDB New Tricks
Fun Teaching MongoDB New Tricks
 
Q
QQ
Q
 
cq_cxf_integration
cq_cxf_integrationcq_cxf_integration
cq_cxf_integration
 
Testando API's de forma unitária mocando as dependências
Testando API's de forma unitária mocando as dependênciasTestando API's de forma unitária mocando as dependências
Testando API's de forma unitária mocando as dependências
 
Servlets
ServletsServlets
Servlets
 
Javascript internals
Javascript internalsJavascript internals
Javascript internals
 

可抽換元件設計模式

  • 2. 相依  名詞:dependency  動詞:depend(s) on  形容詞:dependent on 類別UserAppService相依於 UserRepository及Logger兩個類 別 public class UserAppService { public void AddUser(UserInfo user) { UserRepository repository = new UserRepository(); repository.Add(user); Logger log = new Logger(); Dictionary<string, string> messages = new Dictionary<string, string>(); messages.Add("Action", "建立使用者"); messages.Add("Detail", string.Format("UserId: {0}, UserName: {1}", user.UserId, user.UserName)); messages.Add("LogTime", DateTime.Now.ToString()); log.Write(messages); } }
  • 3. 耦合  Coupling,以程度區分為 loose-coupling / loosely-coupled / 鬆散耦合: 相依於介面(interface) tight-coupling / tightly-coupled / 緊密耦合: 相依於實作(implementation)
  • 5. 一塊蛋糕 public class TextLogger { public void Write(Dictionary<string, string> messages) { StringBuilder sb = new StringBuilder(); messages.ToList().ForEach(c => sb.AppendLine(string.Format("{0}:{1}", c.Key, c.Value))); sb.AppendLine(); File.AppendAllText(string.Format("{0}.txt", DateTime.Now.ToString("yyyyMMdd")), sb.ToString()); } } public class UserAppService { public void AddUser(UserInfo user) { UserRepository repository = new UserRepository(); repository.Add(user); TextLogger log = new TextLogger(); Dictionary<string, string> messages = new Dictionary<string, string>(); messages.Add("Action", "建立使用者"); messages.Add("Detail", string.Format("UserId: {0}, UserName: {1}", user.UserId, user.UserName)); messages.Add("LogTime", DateTime.Now.ToString()); log.Write(messages); } }
  • 7. 仍然是一塊蛋糕 public class XmlLogger { public void Write(Dictionary<string, string> messages) { XElement element = new XElement( new XElement("Record", new XElement("Action", messages["Action"]), new XElement("Detail", messages["Detail"]), new XElement("LogTime", messages["LogTime"]) ) ); File.AppendAllText(string.Format("{0}_{1}.xml", DateTime.Now.ToString("yyyyMMdd"), Guid.NewGuid()), element.ToString()); } } public class UserAppService { public void AddUser(UserInfo user) { … XmlLogger log = new XmlLogger(); … } }
  • 8. WHAT IF AGAIN…  某甲:長官說要改成寫進資料庫唷, ^.<  某乙:…(OS: what the…(╯-_-)╯╧╧ )
  • 9. 翻桌前思考一下  耦 合 度 太 高 。 系 統 提 供 新 的 log 機 制 時 , UserAppService類別就必須修改實作內容以 符合需求(TextLogger->XmlLogger)  彈性不足,無法任意抽換實作機制
  • 10. 解決方案  Plugin Pattern  Provider Pattern (.NET Framework內建)  IoC Pattern
  • 11. PLUGIN PATTERN  Links classes during configuration rather than compilation. – Patterns of Enterprise Application Architecture [P of EAA], p.499 透過反射(reflection)機制於執行期(run time) 由設定檔(configuration)取得實際要執行的物 件 TextLogger / XmlLogger / …
  • 12. PLUGIN PATTERN特色  會有一個實作Factory Method Pattern的類別, 用於生成實際要執行之物件(plugin object)  設定檔內會有實際生成物件的組件名稱及型 別,如放置在App.config或Web.config的 appSettings中  實際生成之物件必定實作一通用介面
  • 13. PLUGIN PATTERN實作(1)  建立一通用介面 public interface ILogger { void Write(Dictionary<string, string> messages); }
  • 14. PLUGIN PATTERN實作(2)  建立TextLogger類別(plugin object),並實作 ILogger介面 public class TextLogger : ILogger { public void Write(Dictionary<string, string> messages) { StringBuilder sb = new StringBuilder(); messages.ToList().ForEach(c => sb.AppendLine(string.Format("{0}:{1}", c.Key, c.Value))); sb.AppendLine(); File.AppendAllText(string.Format("{0}.txt", DateTime.Now.ToString("yyyyMMdd")), sb.ToString()); } }
  • 15. PLUGIN PATTERN實作(3)  建立XmlLogger類別(plugin object) ,並實作 ILogger介面 public class XmlLogger : ILogger { public void Write(Dictionary<string, string> messages) { XElement element = new XElement( new XElement("Record", new XElement("Action", messages["Action"]), new XElement("Detail", messages["Detail"]), new XElement("LogTime", messages["LogTime"]) ) ); File.AppendAllText(string.Format("{0}_{1}.xml", DateTime.Now.ToString("yyyyMMdd"), Guid.NewGuid()), element.ToString()); } }
  • 16. PLUGIN PATTERN實作(4)  建立一實作Factory Method Pattern的類別 public class LoggerFactory { private static ILogger _logger; public static ILogger CreateLogger() { if (_logger == null) { string assemblyName = ConfigurationManager.AppSettings["AssemblyName"]; string classType = ConfigurationManager.AppSettings["ClassType"]; Assembly assembly = Assembly.Load(assemblyName); _logger = assembly.CreateInstance(classType) as ILogger; } return _logger; } }
  • 17.  建立設定檔 <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="AssemblyName" value="LoggerSample"/> <add key="ClassType" value="LoggerSample.PluginPattern.TextLogger"/> </appSettings> </configuration> PLUGIN PATTERN實作(5) Fully qualified name
  • 18.  修改相依TextLogger/XmlLogger/…類別的程 式碼 public class UserAppService { public void AddUser(UserInfo user) { UserRepository repository = new UserRepository(); repository.Add(user); //TextLogger log = new TextLogger(); ILogger log = LoggerFactory.CreateLogger(); Dictionary<string, string> messages = new Dictionary<string, string>(); messages.Add("Action", "建立使用者"); messages.Add("Detail", string.Format("UserId: {0}, UserName: {1}", user.UserId, user.UserName)); messages.Add("LogTime", DateTime.Now.ToString()); log.Write(messages); } } PLUGIN PATTERN實作(6) 相依實作轉為相依介面,耦合度降低
  • 19.  若要切換log機制,僅須修改設定檔 <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="AssemblyName" value="LoggerSample"/> <add key="ClassType" value="LoggerSample.PluginPattern.XmlLogger"/> </appSettings> </configuration> PLUGIN PATTERN實作(7)
  • 20. PLUGIN PATTERN兩三事  一組介面,提供多種實作  通用介面(ILogger)與實際要生成的物件(plugin object)通常會存在於不同的組件中  提供新的plugin object時,不需修改原始程式 碼(UserAppService),僅需提供新的plugin組 件並修改設定檔  plugin object實作內容若有異動,僅需patch其 組件
  • 21. PROVIDER PATTERN  .NET Framework內建的設計模式  提供一組介面,多種實作切換的功能
  • 22. PROVIDER PATTERN特色  會有一個實作Factory Method Pattern的類別, 用於生成實際要執行之物件  會有一個繼承ConfigurationSection類別的類 別且設定檔內會存放可於執行期生成之物件 的組件名稱及型別  實際生成之物件必定繼承ProviderBase類別 並實作一通用介面
  • 23. PROVIDER PATTERN實作(1)  建立一通用介面 public interface ILoggerProvider { void Write(Dictionary<string, string> messages); }
  • 24. PROVIDER PATTERN實作(2)  建 立 TextLoggerProvider 類 別 , 繼 承 ProviderBase類別並實作ILoggerProvider介 面public class TextLoggerProvider : ProviderBase, ILoggerProvider { public void Write(Dictionary<string, string> messages) { StringBuilder sb = new StringBuilder(); messages.ToList().ForEach(c => sb.AppendLine(string.Format("{0}:{1}", c.Key, c.Value))); sb.AppendLine(); File.AppendAllText(string.Format("{0}.txt", DateTime.Now.ToString("yyyyMMdd")), sb.ToString()); } }
  • 25. PROVIDER PATTERN實作(3)  建 立 XmlLoggerProvider 類 別 , 繼 承 ProviderBase類別並實作ILoggerProvider介 面public class XmlLoggerProvider : ProviderBase, ILoggerProvider { public void Write(Dictionary<string, string> messages) { XElement element = new XElement( new XElement("Record", new XElement("Action", messages["Action"]), new XElement("Detail", messages["Detail"]), new XElement("LogTime", messages["LogTime"]) ) ); File.AppendAllText(string.Format("{0}_{1}.xml", DateTime.Now.ToString("yyyyMMdd"), Guid.NewGuid()), element.ToString()); } }
  • 26. PROVIDER PATTERN實作(4)  建 立 LoggerProviderSection 類 別 並 繼 承 ConfigurationSection類別 public class LoggerProviderSection : ConfigurationSection { public const string SectionName = "loggerProvider"; [ConfigurationProperty("providers", IsDefaultCollection = true)] public ProviderSettingsCollection Providers { get { return (ProviderSettingsCollection)base["providers"]; } } [ConfigurationProperty("defaultProvider")] public string DefaultProvider { get { return (string)base["defaultProvider"]; } set { base["defaultProvider"] = value; } } }
  • 27. PROVIDER PATTERN實作(5)  建立一實作Factory Method Pattern的類別 public class LoggerProviderFactory { private static ProviderBase _provider; public static ILoggerProvider CreateLoggerProvider() { if (_provider == null) { LoggerProviderSection section = (LoggerProviderSection)ConfigurationManager.GetSection(LoggerProviderSection.SectionName); ProviderSettings settings = section.Providers[section.DefaultProvider]; _provider = Activator.CreateInstance(Type.GetType(settings.Type)) as ProviderBase; _provider.Initialize(settings.Name, settings.Parameters); } return _provider as ILoggerProvider; } }
  • 28.  建立設定檔,設定預設 provider(defaultProvider)<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="loggerProvider" type="LoggerSample.ProviderPattern.LoggerProviderSection, LoggerSample" /> </configSections> <loggerProvider defaultProvider="xml"> <providers> <add name="text" type="LoggerSample.ProviderPattern.TextLoggerProvider, LoggerSample" /> <add name="xml" type="LoggerSample.ProviderPattern.XmlLoggerProvider, LoggerSample" /> </providers> </loggerProvider> </configuration> PROVIDER PATTERN實作(6) Fully qualified name Assembly name
  • 29. IOC PATTERN  To be continued…
  • 30. 參考  Patterns of Enterprise Application Architecture  Microsoft .NET: Architecting Applications for the Enterprise  Design Patterns: Elements of Reusable Object-Oriented Software  Provider Pattern  撰寫自己的 Configuration 區段 Part 1:不要再賴在 appSettings 的屋簷 下了,寫個自己的 Configuration 區段吧~  Provider Model Design Pattern and Specification, Part 1  ProviderBase Class  Designing loosely coupled components in .NET - Provider Pattern