溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

.NET框架設計—常被忽視的框架設計技巧

發(fā)布時間:2020-07-23 10:12:03 來源:網(wǎng)絡 閱讀:2361 作者:王清培 欄目:編程語言

閱讀目錄:

  • 1.開篇介紹

  • 2.元數(shù)據(jù)緩存池模式(在運行時構(gòu)造元數(shù)據(jù)緩存池)

    • 2.1.元數(shù)據(jù)設計模式(抽象出對數(shù)據(jù)的描述數(shù)據(jù))

    • 2.2.借助Dynamic來改變IOC、AOP動態(tài)綁定的問題

    • 2.3.元數(shù)據(jù)和模型綁定、元數(shù)據(jù)應該隱藏在Model背后、元數(shù)據(jù)與DSL的關(guān)系


  • 3.鏈式配置Dynamic模式(愛不釋手的思維習慣編程)

  • 4.委托工廠模式(要優(yōu)于常見的 工廠,概念更加準確,減少污染)

  • 5.規(guī)則外掛(視委托為特殊的規(guī)則對象原型)

1】開篇介紹

通過上一篇的“.NET框架設計—常被忽視的C#設計技巧”一文來看,對于框架設計的技巧還是有很多人比較有興趣的,那么框架設計思想對于我們?nèi)粘i_發(fā)來說其實并不是很重要,但是對于我們理解框架背后的運行原理至關(guān)重要;當我們使用著LINQ靈活的語法的同時我們是否能理解它的背后運行原理、設計原理更深一點就是它的設計模式及復雜的對象模型;

從一開始學習.NET我就比較喜歡框架背后的設計模型,框架提供給我們的使用接口是及其簡單的,單純從使用上來看我們不會隨著對框架的使用時間而增加我們對框架內(nèi)部設計的理解,反而會養(yǎng)成一樣哪來即用的習慣,我們只有去了解、深挖它的內(nèi)部設計原理才是我們長久學習的目標;因為框架的內(nèi)部設計模式是可以提煉出來并被總結(jié)的;

這篇文章總結(jié)了幾個我最近接觸的框架設計思想,可以稱他們?yōu)槟J?;由于時間關(guān)系,這里只是介紹加一個簡單的介紹和示例讓我們能基本的了解它并且能在日后設計框架的時候想起來有這么一個模式、設計方式可以借鑒;當然,這每一節(jié)都是一個很大主題,用的時候在去細心的分析學習吧;

2】元數(shù)據(jù)緩存池模式(在運行時構(gòu)造元數(shù)據(jù)緩存池)

很多框架都有將特性放在屬性上面用來標識某種東西,但是這種方式使用不當?shù)脑挄π阅茉斐捎绊懀辉購目蚣茉O計原則來講也是對DomainModel極大的污染,從EntityFramework5.0之前的版本我們就能體會到了,它原本是將部分Attribute加在了Entity上的,但是這畢竟是業(yè)務代碼的核心,原則上來說這不能有任何的污染,要絕對的POJO;后來5.0之后就完全獨立了DomainModel.Entity,所有的管理都在運行時構(gòu)造綁定關(guān)系,因為它有EDMX元數(shù)據(jù)描述文件;

那么這些Attribute其實本質(zhì)是.NET在運行時的一種元數(shù)據(jù),主要的目的是我們想在運行時將它讀取出來,用來對某些方面的判斷;那么現(xiàn)在的問題是如果我們每次都去讀取這個Attribute是必須要走反射機制,當然你可以找一些框架來解決這個問題;(我們這里討論的是你作為開發(fā)框架的設計者?。?/span>

反射影響性能這不用多講了,那么常規(guī)的做法是會在第一次反射之后將這些對象緩存起來,下次再用的時候直接在緩存中讀?。贿@沒有問題,這是解決了反射的性能問題,那么你的Attribute是否還要加在DomainModel中呢,如果加的話隨著代碼量的增加,這些都會成為后面維護的成本開銷;那么我們?nèi)绾螌⒏蓛舻腜OJO對象提供給程序員用,但是在后臺我們也能對POJO進行強大的控制?這是否是一種設計問題?

2.1】元數(shù)據(jù)設計模式(抽象出對數(shù)據(jù)的描述數(shù)據(jù))

我一直比較關(guān)注對象與數(shù)據(jù)之間的關(guān)系,面向?qū)ο蟮倪@種縱橫向關(guān)系如何平滑的與E-R實體關(guān)系模型對接,這一直是復雜軟件開發(fā)的核心問題;這里就用它來作為本章的示例的基本概要;

我們有一個基本的DomainModel聚合,如何在不影響本身簡潔性的情況下與E-R關(guān)系對接,比如我們在對聚合進行一個Add操作如何被映射成對數(shù)據(jù)庫的Insert操作;我們來看一下元數(shù)據(jù)設計模式思想;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-04
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ConsoleApplication1.DomainModel
{
    /// <summary>
    /// Employee.<see cref="DomainModel.Employee"/>
    /// </summary>
    public class Employee
    {
        /// <summary>
        /// Primary id.
        /// </summary>
        public string EId { get; set; }
        /// <summary>
        /// Name.
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Sex.<see cref="DomainModel.SexType"/>
        /// </summary>
        public SexType Sex { get; set; }
        /// <summary>
        /// Address.
        /// </summary>
        public Address Address { get; set; }
    }
}
/*============================================================================== * Author:深度訓練 * Create time: 2013-08-04 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領域軟件工程實踐; *==============================================================================*/namespace ConsoleApplication1.DomainModel{/// <summary>/// Employee.<see cref="DomainModel.Employee"/>/// </summary>publicclass Employee    {/// <summary>/// Primary id./// </summary>publicstring EId { get; set; }/// <summary>/// Name./// </summary>publicstring Name { get; set; }/// <summary>/// Sex.<see cref="DomainModel.SexType"/>/// </summary>public SexType Sex { get; set; }/// <summary>/// Address./// </summary>public Address Address { get; set; }    }}


這里有一個以Employee實體為聚合根的聚合,里面包含一些基本的屬性,特別需要強調(diào)的是Sex屬性和Address,這兩個屬性分別是Complex類型的屬性; Complex類型的屬性是符合面向?qū)ο蟮男枰模窃?a title="關(guān)系型數(shù)據(jù)庫" target="_blank" href="http://www.kemok4.com/mysql/">關(guān)系型數(shù)據(jù)庫中是很難實現(xiàn)的,這里就需要我們用元數(shù)據(jù)將它描述出來并能在一些行為上進行控制;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-04
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ConsoleApplication1.DomainModel
{
    /// <summary>
    /// Address .
    /// </summary>
    public struct Address
    {
        /// <summary>
        /// Address name.
        /// </summary>
        public string AddressName { get; set; }
    }
}
/*============================================================================== * Author:深度訓練 * Create time: 2013-08-04 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領域軟件工程實踐; *==============================================================================*/namespace ConsoleApplication1.DomainModel{/// <summary>/// Address ./// </summary>publicstruct Address    {/// <summary>/// Address name./// </summary>publicstring AddressName { get; set; }    }}


這是Address類型的定義;

namespace ConsoleApplication1.DomainModel
{
    /// <summary>
    /// Sex type.
    /// </summary>
    public enum SexType
    {
        Male,
        Female
    }
}
namespace ConsoleApplication1.DomainModel{/// <summary>/// Sex type./// </summary>publicenum SexType    {        Male,        Female    }}


這是SexType類型的定義;都比較簡單;


只有這樣我們才能對DomainModel進行大面積的復雜設計,如果我們不能將數(shù)據(jù)對象化我們無法使用設計模式,也就談不上擴展性;

圖1:

.NET框架設計—常被忽視的框架設計技巧

這是我們的對象模型,那么我們?nèi)绾螌⑺c數(shù)據(jù)庫相關(guān)的信息提取出來形成獨立的元數(shù)據(jù)信息,對元數(shù)據(jù)的抽取需要動、靜結(jié)合才行;

什么動、靜結(jié)合,我們是否都會碰見過這樣的問題,很多時候我們的代碼在編譯時是確定的,但是有部分的代碼需要在運行時動態(tài)的構(gòu)造,甚至有些時候代碼需要根據(jù)當前的IDE來生成才行,但是最終在使用的時候這些在不同階段生成的代碼都需要結(jié)合起來變成一個完整的元數(shù)據(jù)對象;

框架在很多時候需要跟IDE結(jié)合才能使使用變的順手,比如我們在開發(fā)自己的ORM框架如果不能直接嵌入到VisualStudio中的話,用起來會很不爽;當我們用自己的插件去連接數(shù)據(jù)庫并且生成代碼的時候,有部分的元數(shù)據(jù)模型已經(jīng)在代碼中實現(xiàn),但是有部分需要我們動態(tài)的去設置才行;

我們來看一下關(guān)于元數(shù)據(jù)的基礎代碼;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-04
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ORM.Meta
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    /// <summary>
    /// Data source context.
    /// </summary>
    public abstract class DataBaseContext : List<MetaTable>, IDisposable
    {
        /// <summary>
        /// Data base name.
        /// </summary>
        protected string DataBaseName { get; set; }
        /// <summary>
        /// Connection string.
        /// </summary>
        protected string ConnectionString { get; set; }
        /// <summary>
        /// Provider child class add table.
        /// </summary>
        /// <param name="table"></param>
        protected virtual void AddTable(MetaTable table)
        {
            this.Add(table);
        }
        /// <summary>
        /// Init context.
        /// </summary>
        protected virtual void InitContext() { }
        public void Dispose() { }
    }
}
/*============================================================================== * Author:深度訓練 * Create time: 2013-08-04 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領域軟件工程實踐; *==============================================================================*/namespace ORM.Meta{using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;/// <summary>/// Data source context./// </summary>publicabstractclass DataBaseContext : List<MetaTable>, IDisposable    {/// <summary>/// Data base name./// </summary>protectedstring DataBaseName { get; set; }/// <summary>/// Connection string./// </summary>protectedstring ConnectionString { get; set; }/// <summary>/// Provider child class add table./// </summary>/// <param name="table"></param>protectedvirtualvoid AddTable(MetaTable table)        {this.Add(table);        }/// <summary>/// Init context./// </summary>protectedvirtualvoid InitContext() { }publicvoid Dispose() { }    }}


這表示數(shù)據(jù)源上下文,屬于運行時元數(shù)據(jù)的基礎設施;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-04
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ORM.Meta
{
    using System.Collections.Generic;
    using System.Linq;
    /// <summary>
    /// Database Table meta.
    /// </summary>
    public class MetaTable : List<MetaColumn>
    {
        /// <summary>
        /// Table name.
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Entity name.
        /// </summary>
        public string EntityName { get; set; }
        /// <summary>
        /// Get column by column name.
        /// </summary>
        /// <param name="name">Column name.</param>
        /// <returns><see cref="ORM.MetaColumn"/></returns>
        public MetaColumn GetColumnByName(string name)
        {
            var column = from item in this.ToList() where item.CoumnName == name select item;
            return column.FirstOrDefault();
        }
    }
}
/*============================================================================== * Author:深度訓練 * Create time: 2013-08-04 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領域軟件工程實踐; *==============================================================================*/namespace ORM.Meta{using System.Collections.Generic;using System.Linq;/// <summary>/// Database Table meta./// </summary>publicclass MetaTable : List<MetaColumn>    {/// <summary>/// Table name./// </summary>publicstring Name { get; set; }/// <summary>/// Entity name./// </summary>publicstring EntityName { get; set; }/// <summary>/// Get column by column name./// </summary>/// <param name="name">Column name.</param>/// <returns><see cref="ORM.MetaColumn"/></returns>public MetaColumn GetColumnByName(string name)        {            var column = from item inthis.ToList() where item.CoumnName == name select item;return column.FirstOrDefault();        }    }}

簡單的表示一個Table,里面包含一系列的Columns;要記住在設計元數(shù)據(jù)基礎代碼的時候?qū)⒔涌诹舫鰜?,方便在IDE中植入初始化元數(shù)據(jù)代碼;

圖2:

.NET框架設計—常被忽視的框架設計技巧

到目前為止我們都是在為元數(shù)據(jù)做基礎工作,我們看一下有系統(tǒng)生成的聲明的元數(shù)據(jù)代碼;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-04
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ConsoleApplication1.Repository
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    /// <summary>
    /// IDE Builder.
    /// </summary>
    public class DesignBuilder_DataBaseContext : ORM.Meta.DataBaseContext
    {
        //this begin IDE builder.
        protected override void InitContext()
        {
            ORM.Meta.MetaTable metaTable = new ORM.Meta.MetaTable() { Name = "TB_Employee", EntityName = "Employee" };
            metaTable.Add(new ORM.Meta.MetaColumn()
            {
                CoumnName = "EId",
                DataType = ORM.Meta.DataType.NVarchar
            });
            metaTable.Add(new ORM.Meta.MetaColumn()
            {
                CoumnName = "Name",
                DataType = ORM.Meta.DataType.NVarchar
            });
            metaTable.Add(new ORM.Meta.MetaColumn()
            {
                CoumnName = "Sex",
                DataType = ORM.Meta.DataType.Int
            });
            metaTable.Add(new ORM.Meta.MetaColumn()
            {
                CoumnName = "Address",
                DataType = ORM.Meta.DataType.NVarchar
            });
            this.AddTable(metaTable);
        }
        //end
    }
}
/*============================================================================== * Author:深度訓練 * Create time: 2013-08-04 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領域軟件工程實踐; *==============================================================================*/namespace ConsoleApplication1.Repository{using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;/// <summary>/// IDE Builder./// </summary>publicclass DesignBuilder_DataBaseContext : ORM.Meta.DataBaseContext    {//this begin IDE builder.protectedoverridevoid InitContext()        {            ORM.Meta.MetaTable metaTable = new ORM.Meta.MetaTable() { Name = "TB_Employee", EntityName = "Employee" };            metaTable.Add(new ORM.Meta.MetaColumn()            {                CoumnName = "EId",                DataType = ORM.Meta.DataType.NVarchar            });            metaTable.Add(new ORM.Meta.MetaColumn()            {                CoumnName = "Name",                DataType = ORM.Meta.DataType.NVarchar            });            metaTable.Add(new ORM.Meta.MetaColumn()            {                CoumnName = "Sex",                DataType = ORM.Meta.DataType.Int            });            metaTable.Add(new ORM.Meta.MetaColumn()            {                CoumnName = "Address",                DataType = ORM.Meta.DataType.NVarchar            });this.AddTable(metaTable);        }//end}}


我假設這是我們框架在IDE中生成的部分元數(shù)據(jù)代碼,當然你可以用任何方式來存放這些元數(shù)據(jù),但是最后還是要去對象化;

圖3:

.NET框架設計—常被忽視的框架設計技巧

這個目錄你可以直接隱藏,在后臺屬于你的框架需要的一部分,沒有必要讓它污染項目結(jié)構(gòu),當然放出來也有理由;如果想讓你的LINQ或者表達式能直接穿過你的元數(shù)據(jù)上下文你需要直接擴展;

static void Main(string[] args)
        {
            using (Repository.DesignBuilder_DataBaseContext context = new Repository.DesignBuilder_DataBaseContext())
            {
                var employee = from emp in context.Employee where emp.EId == "Wqp123" select emp;
            }
        }
staticvoid Main(string[] args)        {using (Repository.DesignBuilder_DataBaseContext context = new Repository.DesignBuilder_DataBaseContext())            {                var employee = from emp in context.Employee where emp.EId == "Wqp123" select emp;            }        }


這里所有的代碼看上去很簡單,沒有多高深的技術(shù),這也不是本篇文章的目的,任何代碼都需要設計的驅(qū)動才能產(chǎn)生價值,我們構(gòu)建的基礎代碼都是元數(shù)據(jù)驅(qū)動;當你在運行時把這些元數(shù)據(jù)放入Cache,既不需要加Attribute也不需要反射反而活動了更大程度上的控制,但是要想構(gòu)建一個能用的元數(shù)據(jù)結(jié)構(gòu)需要結(jié)合具體的需求才行;

2.2】借助Dynamic來改變IOC、AOP動態(tài)綁定的問題

要想在運行時完全動態(tài)的綁定在編譯時定義的對象行為是需要強大的IOC框架支撐的,這樣的框架我們是做不來的或者需要很多精力,得不償失;對于元數(shù)據(jù)設計需要將AOP通過IOC的方式注入,在使用的時候需要改變一下思路,AOP的所有的切面在編譯時無法確定,后期通過IOC的方式將所有的行為注入;這里我們需要使用動態(tài)類型特性;

使用Dynamic之后我們很多以往不能解決問題都可以解決,更向元編程跨進了一步;對于IOC、AOP的使用也將變的很簡單,也有可能顛覆以往IOC、AOP的使用方式;而且動態(tài)編程將在很大程度上越過設計模式了,也就是設計模式的使用方式在動態(tài)編程中將不復存在了;

using (Repository.DesignBuilder_DataBaseContext context = new Repository.DesignBuilder_DataBaseContext())
            {
                var employees = from emp in context.Employee where emp.EId == "Wqp123" select emp;
                Employee employee = new Employee() { EId = "Wqp123" };
                var entityOpeartion = DynamicBehavior.EntityDymanicBehavior.GetEntityBehavior<Employee>(employee);
                entityOpeartion.Add();
            }
using (Repository.DesignBuilder_DataBaseContext context = new Repository.DesignBuilder_DataBaseContext())            {                var employees = from emp in context.Employee where emp.EId == "Wqp123" select emp;                Employee employee = new Employee() { EId = "Wqp123" };                var entityOpeartion = DynamicBehavior.EntityDymanicBehavior.GetEntityBehavior<Employee>(employee);                entityOpeartion.Add();            }



/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-04
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ConsoleApplication1.DynamicBehavior
{
    using System;
    using System.Dynamic;
    public class EntityDymanicBehavior
    {
        public static dynamic GetEntityBehavior<TEntity>(TEntity entity)
        {
            //auto mark entity behavior
            dynamic dy = new ExpandoObject();
            //load meta data mark dynamic behavior
            dy.Entity = entity;
            dy.Add = new Action(() =>
            {
                Console.WriteLine("Action Add " + entity.GetType());
            });
            return dy;
        }
    }
}
/*============================================================================== * Author:深度訓練 * Create time: 2013-08-04 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領域軟件工程實踐; *==============================================================================*/namespace ConsoleApplication1.DynamicBehavior{using System;using System.Dynamic;publicclass EntityDymanicBehavior    {publicstatic dynamic GetEntityBehavior<TEntity>(TEntity entity)        {//auto mark entity behaviordynamic dy = new ExpandoObject();//load meta data mark dynamic behaviordy.Entity = entity;            dy.Add = new Action(() =>            {                Console.WriteLine("Action Add " + entity.GetType());            });return dy;        }    }}


圖4:

.NET框架設計—常被忽視的框架設計技巧

畫紅線的部分是可以抽取來放入擴展方法Add中的,在構(gòu)造的內(nèi)部是完全可以進入到元數(shù)據(jù)緩存池中拿到這些數(shù)據(jù)然后直接動態(tài)生成擴展方法背后的真實方法;

2.3】元數(shù)據(jù)和模型綁定、元數(shù)據(jù)應該隱藏在Model背后、元數(shù)據(jù)與DSL的關(guān)系

元數(shù)據(jù)的綁定應該在運行時動態(tài)去完成,這點在以往我們需要大費力氣,通過CodeDom、Emit才能完成,但是現(xiàn)在可以通過Dynamic、DLR來完成;思維需要轉(zhuǎn)變一下,動態(tài)編程我們以往用的最多的地方在JS上,現(xiàn)在可以在C#中使用,當然你也可以使用專門的動態(tài)語言來寫更強大的元數(shù)據(jù)框架,IronRuby、IronPython都是很不錯的,簡單的了解過Ruby的元數(shù)據(jù)編程,很強大,如果我們.NET程序員眼饞就用Iron…系列;

在開發(fā)復雜的動態(tài)行為時盡量使用元數(shù)據(jù)設計思想,不要把數(shù)據(jù)和表示數(shù)據(jù)的數(shù)據(jù)揉在一起,要把他們分開,在運行時Dynamic綁定;元數(shù)據(jù)應該在Model的背后應該在DomainModel的背后;

元數(shù)據(jù)和DSL有著天然的淵源,如果我們能把所有的語句組件化就可以將其封入.NET組件中,在IDE中進行所見即所得的DSL設計,然后生成可以直接運行的Dynamic代碼,這可能也是元編程的思想之一吧;

圖5:

.NET框架設計—常被忽視的框架設計技巧

這可能是未來10年要改變的編程路線吧,我只是猜測;最后軟件將進一步被自定義;

3】鏈式配置Dynamic模式(愛不釋手的思維習慣編程)

再一次提起鏈式編程是覺得它的靈活性無話可說,語言特性本身用在哪里完全需求驅(qū)動;把鏈式用來做配置相關(guān)的工作非常的合適;我們上面做了元數(shù)據(jù)配置相關(guān)的工作,這里我們試著用鏈式的方法來改善它;

Dynamic類型本身的所有行為屬性都是可以動態(tài)構(gòu)建的,那么我們把它放入鏈式的方法中去,根據(jù)不同的參數(shù)來實現(xiàn)動態(tài)的添加行為;

擴展Dynamic類型需要使用ExpandoObject開始;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-04
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ConsoleApplication1.DynamicBehavior
{
    using System;
    using System.Dynamic;
    public static class EntityDynamicBehaviorExtent
    {
        /// <summary>
        /// Add dynamic method.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static ExpandoObject AddExten(this ExpandoObject entity)
        {
            dynamic dy = entity as dynamic;
            dy.Add = new Func<ExpandoObject>(() => { Console.WriteLine("add " + entity); return entity; });
            return entity;
        }
        /// <summary>
        /// where  dynamic method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <param name="where"></param>
        /// <returns></returns>
        public static ExpandoObject WhereExten<T>(this ExpandoObject entity, Func<T, bool> where)
        {
            dynamic dy = entity as dynamic;
            dy.Where = where;
            return entity;
        }
    }
}
/*============================================================================== * Author:深度訓練 * Create time: 2013-08-04 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領域軟件工程實踐; *==============================================================================*/namespace ConsoleApplication1.DynamicBehavior{using System;using System.Dynamic;publicstaticclass EntityDynamicBehaviorExtent    {/// <summary>/// Add dynamic method./// </summary>/// <param name="entity"></param>/// <returns></returns>publicstatic ExpandoObject AddExten(this ExpandoObject entity)        {            dynamic dy = entity as dynamic;            dy.Add = new Func<ExpandoObject>(() => { Console.WriteLine("add " + entity); return entity; });return entity;        }/// <summary>/// where  dynamic method. /// </summary>/// <typeparam name="T"></typeparam>/// <param name="entity"></param>/// <param name="where"></param>/// <returns></returns>publicstatic ExpandoObject WhereExten<T>(this ExpandoObject entity, Func<T, bool> where)        {            dynamic dy = entity as dynamic;            dy.Where = where;return entity;        }    }}


擴展方法需要擴展 ExpandoObject對象,DLR在運行時使用的是ExpandoObject對象實例,所以我們不能夠直接擴展Dynamic關(guān)鍵字;

Employee employee1 = new Employee() { EId = "Wqp123" };
                var dynamicEntity = DynamicBehavior.EntityDymanicBehavior.GetEntityBehavior<Employee>(employee1);
                (dynamicEntity as System.Dynamic.ExpandoObject).AddExten().WhereExten<Employee>(emp =>
                {
                    Console.WriteLine("Where Method.");
                    return emp.EId == "Wqp123";
                });
                dynamicEntity.Add().Where(employee1);
 Employee employee1 = new Employee() { EId = "Wqp123" };                var dynamicEntity = DynamicBehavior.EntityDymanicBehavior.GetEntityBehavior<Employee>(employee1);                (dynamicEntity as System.Dynamic.ExpandoObject).AddExten().WhereExten<Employee>(emp =>                {                    Console.WriteLine("Where Method.");return emp.EId == "Wqp123";                });                dynamicEntity.Add().Where(employee1);


圖6:

.NET框架設計—常被忽視的框架設計技巧

紅線部分必須要轉(zhuǎn)換才能順利添加行為;

4】委托工廠模式(要優(yōu)于常見的 工廠,概念更加準確,減少污染)

對于工廠模式我們都會熟悉的一塌糊涂,各種各樣的工廠模式我們見的多了,但是這種類型的工廠使用方式你還真的沒見過;其實這種委托是想部分的邏輯交給外部來處理;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-04
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ConsoleApplication1.DomainModel
{
    /// <summary>
    /// Address factory.
    /// </summary>
    /// <returns></returns>
    public delegate Address Factory();
    /// <summary>
    /// Employee.<see cref="DomainModel.Employee"/>
    /// </summary>
    public class Employee
    {
        public Employee() { }
        /// <summary>
        /// Mark employee instance.
        /// </summary>
        /// <param name="eID"></param>
        /// <param name="name"></param>
        /// <param name="sex"></param>
        /// <param name="addressFactory">address factory.</param>
        public Employee(string eID, string name, SexType sex, Factory addressFactory)
        {
            this.EId = eID;
            this.Name = name;
            this.Sex = sex;
            this.Address = addressFactory();
        }
        /// <summary>
        /// Primary id.
        /// </summary>
        public string EId { get; set; }
        /// <summary>
        /// Name.
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Sex.<see cref="DomainModel.SexType"/>
        /// </summary>
        public SexType Sex { get; set; }
        /// <summary>
        /// Address.
        /// </summary>
        public Address Address { get; set; }
    }
}
/*============================================================================== * Author:深度訓練 * Create time: 2013-08-04 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領域軟件工程實踐; *==============================================================================*/namespace ConsoleApplication1.DomainModel{/// <summary>/// Address factory./// </summary>/// <returns></returns>publicdelegate Address Factory();/// <summary>/// Employee.<see cref="DomainModel.Employee"/>/// </summary>publicclass Employee    {public Employee() { }/// <summary>/// Mark employee instance./// </summary>/// <param name="eID"></param>/// <param name="name"></param>/// <param name="sex"></param>/// <param name="addressFactory">address factory.</param>public Employee(string eID, string name, SexType sex, Factory addressFactory)        {this.EId = eID;this.Name = name;this.Sex = sex;this.Address = addressFactory();        }/// <summary>/// Primary id./// </summary>publicstring EId { get; set; }/// <summary>/// Name./// </summary>publicstring Name { get; set; }/// <summary>/// Sex.<see cref="DomainModel.SexType"/>/// </summary>public SexType Sex { get; set; }/// <summary>/// Address./// </summary>public Address Address { get; set; }    }}


我們定義了一個用來創(chuàng)建Employee.Address對象的Factory,然后通過構(gòu)造函數(shù)傳入;

Employee employee2 = new Employee("Wqp123", "Wqp", SexType.Male, new Factory(() =>
        {
              return new Address() { AddressName = "Shanghai" };
         }));
Employee employee2 = new Employee("Wqp123", "Wqp", SexType.Male, new Factory(() =>                {returnnew Address() { AddressName = "Shanghai" };                }));


這里純粹為了演示方便,這種功能是不應該在DommianModel中使用的,都是在一些框架、工具中用來做靈活接口用的;

5】規(guī)則外掛(視委托為特殊的規(guī)則對象原型)

規(guī)則外掛其實跟上面的委托工廠有點像,但是絕對不一樣的設計思想;如何將規(guī)則外掛出去,放入Cache中讓運行時可以配置這個規(guī)則參數(shù);委托是規(guī)則的天然宿主,我們只要將委托序列化進Cache就可以對它進行參數(shù)的配置;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-04
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ConsoleApplication1.DomainModel.Specification
{
    using System;
    using System.Linq.Expressions;
    /// <summary>
    /// Employee add specification.
    /// </summary>
    [Serializable]
    public class EmployeeSpecificationAdd : System.Runtime.Serialization.IDeserializationCallback
    {
        /// <summary>
        /// specification.
        /// </summary>
        [NonSerialized]
        private Func<Employee, bool> _specification;
        /// <summary>
        /// Gets specification.
        /// </summary>
        public Func<Employee, bool> Specificaion { get { return _specification; } }
        /// <summary>
        /// employee.
        /// </summary>
        private Employee Employee { get; set; }
        /// <summary>
        /// Mark employee specificatoin.
        /// </summary>
        /// <param name="employee"></param>
        public EmployeeSpecificationAdd(Employee employee)
        {
            this.Employee = employee;
            InitSpecification();
        }
        /// <summary>
        /// Is Check.
        /// </summary>
        /// <returns></returns>
        public bool IsCheck()
        {
            return _specification(Employee);
        }
        public void OnDeserialization(object sender)
        {
            InitSpecification();
        }
        private void InitSpecification()
        {
            this._specification = (emp) =>
            {
                return !string.IsNullOrWhiteSpace(emp.EId) && !string.IsNullOrWhiteSpace(emp.Name);
            };
        }
    }
}
/*============================================================================== * Author:深度訓練 * Create time: 2013-08-04 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ * Author Description:特定領域軟件工程實踐; *==============================================================================*/namespace ConsoleApplication1.DomainModel.Specification{using System;using System.Linq.Expressions;/// <summary>/// Employee add specification./// </summary>[Serializable]publicclass EmployeeSpecificationAdd : System.Runtime.Serialization.IDeserializationCallback    {/// <summary>/// specification./// </summary>[NonSerialized]private Func<Employee, bool> _specification;/// <summary>/// Gets specification./// </summary>public Func<Employee, bool> Specificaion { get { return _specification; } }/// <summary>/// employee./// </summary>private Employee Employee { get; set; }/// <summary>/// Mark employee specificatoin./// </summary>/// <param name="employee"></param>public EmployeeSpecificationAdd(Employee employee)        {this.Employee = employee;            InitSpecification();        }/// <summary>/// Is Check./// </summary>/// <returns></returns>publicbool IsCheck()        {return _specification(Employee);        }publicvoid OnDeserialization(object sender)        {            InitSpecification();        }privatevoid InitSpecification()        {this._specification = (emp) =>            {return !string.IsNullOrWhiteSpace(emp.EId) && !string.IsNullOrWhiteSpace(emp.Name);            };        }    }}


圖7:


.NET框架設計—常被忽視的框架設計技巧

注意這里的反序列化接口實現(xiàn),因為Lambda無法進行序列化,也沒有必要進行序列化;

EmployeeSpecificationAdd specification = new EmployeeSpecificationAdd(employee2);
                Stream stream = File.Open("specification.xml", FileMode.Create);
                BinaryFormatter formattter = new BinaryFormatter();
                formattter.Serialize(stream, specification);
                stream.Seek(0, SeekOrigin.Begin);
                specification = formattter.Deserialize(stream) as EmployeeSpecificationAdd;
                stream.Close();
                stream.Dispose();
                if (specification.IsCheck())
                {
                    Console.WriteLine("Ok...");
                }
EmployeeSpecificationAdd specification = new EmployeeSpecificationAdd(employee2);                Stream stream = File.Open("specification.xml", FileMode.Create);                BinaryFormatter formattter = new BinaryFormatter();                formattter.Serialize(stream, specification);                stream.Seek(0, SeekOrigin.Begin);                specification = formattter.Deserialize(stream) as EmployeeSpecificationAdd;                stream.Close();                stream.Dispose();if (specification.IsCheck())                {                    Console.WriteLine("Ok...");                }


既然能將規(guī)則序列化了,就可以把它放在任何可以使用的地方了,配置化已經(jīng)沒有問題了;

示例Demo地址:http://files.cnblogs.com/wangiqngpei557/ConsoleApplication2.zip

作者:王清培

出處:http://wangqingpei557.blog.51cto.com/

本文版權(quán)歸作者和51CTO共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權(quán)利。




向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI