企业代码

企业代码需要我们用以下方式重新看待代码

一、模块性   

代码单元通常表现为类或类型。它们是基于特定目标而设计的。各个代码单元间的交互方式既要能达到较大的期望目标,    又不能违背对它们进行划分的准则。模块化不仅是针对可重用性的代码分离,同时也要求很强的松散耦合度。

二、松散耦合的类    

如果代码单元需要使用来自系统其他部分的服务,那么这些服务应该抽象地由传递到该单元中。创建所需的依懒不应该是该单元的职责。  如果针对代码单元编写单元测试很方便,就证明其松散耦合度很低。

看下面的示例:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Web;

namespace ProfessionalEnterprise.Net.Chapter02
{
    public class Program
    {
        const double basePrice = 35.00;
        static void Main(string[] args) {
            DataSet ds = new DataSet();
            OleDbCommand cmd = new OleDbCommand();
            string sql = "Select c.LastName,c.FristName,c.MiddleName,c.CustmerRegion,e.EmailAddress from Customers c inner join Email e on c.ID = e.CustomerID Order by c.LastName ASC";
            string name = "";
            double price = 0.0;
            OleDbConnection conn = new OleDbConnection();
            OleDbDataAdapter adapter = new OleDbDataAdapter();
            cmd.Connection = conn;
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = sql;
            adapter.SelectCommand = cmd;

            try
            {
                adapter.Fill(ds);
            }
            catch (Exception e) {
                Console.WriteLine(e.Message);
            }

            foreach (DataRow dr in ds.Tables[0].Rows) {
                name = String.Format("{0},{1}{2}", dr["LastName"], dr["FirstName"], dr["MiddleName"]);
                switch (dr["CustomerRegion"].ToString()) { 
                    case "1":
                        price = (0.9 * basePrice);
                        break;
                    case "2":
                        price = (0.85 * basePrice);
                        break;
                    case "3":
                        price = (0.8 * basePrice);
                        break;
                }

                Console.WriteLine(String.Format("Customer name:{0} Customer Email Address:{1} Customer's Price:{3}", name, dr["EmailAddress"].ToString(), price));
            }
        }

    }
}

Program中的代码可以正常工作,但不是良好设计的代码。它不仅严重违背了模块化规则,还违背了松散耦合的基本准则。  在本例中,我们无法编写单元测试。

 对于同一示例,一个好的多的版本可能要分解为不同的类型,每个类型能够符合模块化和松散耦合的规则。

首先,我们创建一个简单的数据实用工具类型:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Web;

namespace ProfessionalEnterprise.Net.Chapter02
{
    /// <summary>
    /// 一个简单的数据实用工具类型
    /// 执行简单的数据提取,并以ADO.NET数据行集的形式返回提取的数据
    /// </summary>
    public class DataUtility
    {
        private string _connectionString;
        private DataSet _ds;
        private OleDbCommand _cmd;
        private OleDbConnection _conn;
        private OleDbDataAdapter _adapter;

        public DataUtility(string connectionString) {
            _connectionString = connectionString;
        }

        public DataRowCollection GetData(string sql) {
            _ds = new DataSet();
            _conn = new OleDbConnection(_connectionString);
            _cmd = new OleDbCommand();
            _cmd.Connection = _conn;
            _cmd.CommandType = CommandType.Text;
            _cmd.CommandText = sql;
            _adapter = new OleDbDataAdapter(_cmd);

            try
            {
                _adapter.Fill(_ds);
            }
            catch 
            { 
               //handle exception and log the event
            }

            return _ds.Tables[0].Rows;
        }
    }
}
View Code

下一步,我们创建一个完成基于基本价格进行打折率计算。由于这些计算代表一组业务规则,因此,实际上不同的上下文下可能需要不同的计算类。因此真正要做的是定义一组规则,抽象地代表这些计算类。可以使用下面这样一个接口:

using System;
using System.Collections.Generic;
using System.Text;

namespace ProfessionalEnterprise.Net.Chapter02
{
    /// <summary>
    /// 定义一组规则,抽象地代表所有计算类
    /// </summary>
    public interface iCalcList
    {
         double this[int region] { get; }
        double GetPrice(int region);
        double BasePrice { get; set; }
    }
}
View Code

在建立企业系统时,该抽象接口是一个重要的设计要素。抽象接口是类型解除关联的流行方法。此时,我们需要创建一个打折列表类:

using System;
using System.Collections.Generic;
using System.Web;

namespace ProfessionalEnterprise.Net.Chapter02
{
    public class DiscountList:iCalcList
    {
        private Dictionary<int, double> _rates;
        private double _basePrice;

        public double BasePrice {
            get { return this._basePrice; }
            set { this._basePrice = value; }
        }

        public DiscountList() {
            _rates = new Dictionary<int, double>();
            _rates.Add(1, 0.9);
            _rates.Add(2, 0.85);
            _rates.Add(3, 0.8);
        }

        public double this[int region] {
            get { return _rates[region]; }
        }

        public double GetPrice(int region) {
            return _rates[region] * BasePrice;
        }
    }
}
View Code

我们需要建立的下一个类应该实现来自数据行的文本格式化。我们需要循环处理每个数据行,提取相关列值,根据客户所在的区域计算相应的价格,然后打印成一个文本行。这个类同样代表一个业务逻辑单元,因此,我们要先定义一个代表这个抽象类型的接口,然后实现文本格式化器。

using System;
using System.Collections.Generic;
using System.Text;

namespace ProfessionalEnterprise.Net.Chapter02
{
    /// <summary>
    /// 数据行的文本格式化抽象逻辑
    /// </summary>
    public interface iTextFormatter
    {
        string GetFormattedText();
    }
}
View Code

下面我们建立文本格式化器本身的代码:

using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Web;

namespace ProfessionalEnterprise.Net.Chapter02
{
    /// <summary>
    /// 文本格式化器
    /// </summary>
    public class DataToTextFormatter:iTextFormatter
    {
        private DataRowCollection _rows;
        private iCalcList _rateCalculator;

        public DataToTextFormatter(DataRowCollection rows, iCalcList rateCalculator) {
            _rows = rows;
            _rateCalculator = rateCalculator;
        }

        private string FormatLineEntry(DataRow dr) {
            string name = String.Format("{0},{1}{2}", dr["LastName"], dr["FirstName"], dr["MiddleName"]);

            double price = _rateCalculator.GetPrice((int)(dr["CustomerRegion"]));
            string email = dr["EmailAddress"].ToString();

            return String.Format("Customer name:{0} Customer Email Address:{1} Customer's Price:{3}", name, email, price);

        }

        public string GetFormattedText() {
            StringBuilder sb = new StringBuilder();
            foreach (DataRow dr in _rows) {
                sb.Append(FormatLineEntry(dr));
            }
            return sb.ToString();
        }
    }
}
View Code

仔细观察示例会发现,构造函数需要一个iCalcList的一个实例,那么构造函数可以很容易的接受iCalcList的子类的一个实例。

下面我们封装一个完成任务的所有单元,我们也可以在控制台应用程序的Main方法中调用它们:

using System;
using System.Collections.Generic;
using System.Web;

namespace ProfessionalEnterprise.Net.Chapter02
{
    /// <summary>
    /// 用于执行相关动作
    /// </summary>
    public class ListPresenter
    {
        private DataUtility _dUtil;
        private iCalcList _dList;
        private iTextFormatter _formatter;

        public ListPresenter(DataUtility dUtil,iCalcList dList,iTextFormatter formatter) {
            this._dList = dList;
            this._dUtil = dUtil;
            this._formatter = formatter;
        }

        public string GetList() {
            string sql = "Select c.LastName,c.FristName,c.MiddleName,c.CustmerRegion,e.EmailAddress from Customers c inner join Email e on c.ID = e.CustomerID Order by c.LastName ASC";
            _dUtil = new DataUtility("读取配置文件获取数据库连接字符串");
            _dList = new DiscountList();
            _dList.BasePrice = 35.0;
            _formatter = new DataToTextFormatter(_dUtil.GetData(sql),_dList);

            return _formatter.GetFormattedText();

        }
    }
}
View Code

 

三、单元测试
  单元测试,引入测试框架来构建自动测试框架。

四、控制反转(Inversion of Control,Ioc)容器,也称为依赖注入(Dependency Injection,DI)容器
     用于将对象的依赖实例传递给松散耦合类。有助于保持代码的模块性,同时自动省去需要我们手动编写的逻辑。

     让我们看一下两种不同的方法。

using System;
using System.Collections.Generic;
using System.Web;

namespace ProfessionalEnterprise.Net.Chapter02.IoC
{
    class ComplexObject
    {
        private ObjA _calssA;
        internal ObjA ClassA
        {
            get { return _calssA; }
            set { _calssA = value; }
        }
        private ObjB _calssB;
        internal ObjB ClassB
        {
            get { return _calssB; }
            set { _calssB = value; }
        }
        private ObjC _calssC;
        internal ObjC ClassC
        {
            get { return _calssC; }
            set { _calssC = value; }
        }
        public ComplexObject()
        {
            _calssA = new ObjA();
            _calssB = new ObjB();
            _calssC = new ObjC();
        }

    }
    class ObjA
    {

    }
    class ObjB
    {

    }
    class ObjC
    {
        private ObjD _calssD;
        internal ObjD ClassD
        {
            get { return _calssD; }
            set { _calssD = value; }
        }
        public ObjC()
        {
            _calssD = new ObjD();
        }
    }
    class ObjD
    {
        private int _count;
        public int Count
        {
            get { return _count; }
            set { _count = value; }
        }
    }
}
View Code

 最后的代码可能如下所示:
   ComplexObject obj = new ComplexObject();
   obj.ClassC.ClassD.Count=5;
   这段代码简单优美,但是违背了模块化和松散耦合的准则,使得我们不能对每个类进行测试。

 对于同样的代码,一个更清晰的版本可能如下:

using System;
using System.Collections.Generic;
using System.Web;

namespace ProfessionalEnterprise.Net.Chapter02.IoC
{
    class ComplexObject
    {
        private ObjA _calssA;
        internal ObjA ClassA
        {
            get { return _calssA; }
            set { _calssA = value; }
        }
        private ObjB _calssB;
        internal ObjB ClassB
        {
            get { return _calssB; }
            set { _calssB = value; }
        }
        private ObjC _calssC;
        internal ObjC ClassC
        {
            get { return _calssC; }
            set { _calssC = value; }
        }
        public ComplexObject(ObjA a,ObjB b,ObjC c)
        {
            _calssA = a;
            _calssB = b;
            _calssC = c;
        }

    }
    class ObjA
    {

    }
    class ObjB
    {

    }
    class ObjC
    {
        private ObjD _calssD;
        internal ObjD ClassD
        {
            get { return _calssD; }
            set { _calssD = value; }
        }
        public ObjC(ObjD d)
        {
            _calssD = d;
        }
    }
    class ObjD
    {
        private int _count;
        public int Count
        {
            get { return _count; }
            set { _count = value; }
        }
    }
}
View Code

使用时代码如下:

 ObjA A = new ObjA();  

 ObjB B = new ObjB();  

 ObjD D = new ObjD();    

ObjC C = new ObjC(D);

ComplexObject obj = new ComplexObject(A,B,C);

       在使用其他对象的类中,需要多的多的代码,而这些代码只是为了创建ComplexObject的一个实例。这正说明了松散耦合设计的好处和它的不足。松散耦合和快速开发之间的折中可以通过使用控制倒置容器来实现。Ioc容器为开发人员提供了一组在编译时对对象编译描述的全局定义。这种容器可以自动感知定义文件中的所有对象和对象依赖关系。当一个使用其他对象的方法或类需要创建复杂对象的时,这些方法和类只需要向IoC容器请求这些对象的实例,请求过程就是调用容器的get()方法,而不是常见的new操作符。下面是针对ComplexObject的IoC容器对象定义的一个示例,该示例来自于流行的Spring.NET IoC容器:

<configuration>
  <configSections>
    <sectionGroup name="spring">
      <section name="context" type="Spring.Context.Support.ContextHandler,Spring.Core"/>
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler,Spring.Core"/>
    </sectionGroup>
  </configSections>
  <spring>
    <context>
      <resource uri="config://spring/objects"/>
    </context>
    <objects xmlns="http://www.springframework.net">

    </objects>

  </spring>
</configuration>

一旦在容器中定义了对象,就可以通过容器引用来对该对象进行初始化:

IApplicationContext Context = ContextRegistry.GetContext();
ComplexObject obj = (ComplexObject)Context.GetObject("etlPipelineEngine");

  IApplicationContext是ComplexObject对象使用上下文的一种Spring.NET定义。一旦拥有了一个IApplicationContext的句柄,就可以简单的使用IApplicationContext的各种静态Get()方法通过名称来调用对象。IoC容器的好处不仅仅是对象实例化简单,还有助于开发使用对象的代码,为对象模型创建清晰的路线图,甚至提供各种层面的面向对象编程。

 

 

 

posted @ 2013-05-18 23:39  jingjunfeng  阅读(1155)  评论(0编辑  收藏  举报