完美开发模型之贫血模型参考(I)

不用争论哪种模型好,而是在每种模型下面怎么样做到最好.
对于企业来说,高效的赢利才是根本,在开发过程中应该追求中庸之道才能达到技术与赢利效率的平衡. 软件过于平铺简单会导致后期维护开发成本大大提升,尽管前期看起来很快;软件设计过于复杂导致整个过程有很大的成本浪费. 具体点应该坚持两个原则:

1. 绝对避免过程式开发:停留在脚本语句,asp之类解释性语言的编程模式上不可取.

2. 尽量避免引入各种框架: 编程模型跟着官方走,别找一大堆外在框架堆积在系统中.

对应的采取的方法是:

1. 充分利用现代的,成熟的面各对象的语言,充分利用三个特性:封装,继承,多态. 就能完美的解决80%的问题,剩下20%的问题由设计模式解决.

2. 充分理解各种框架的原理,然后根据应用规模自己模拟其核心实现一个精简版本,就能完全解决各种性能,扩展性,可靠性,效率等软件质量方面的问题.

如果不按照上面做也会导致两个非常糟糕的局面.

1. 随着项目的庞大和时间和流长,代码累积乱成麻:一是不敢轻易对核心业务的乱代码进行升级修改,二是在修改的时候要花费大量时间去理解原来过程式代码,三是非常多的垃圾无用代码没人能清理,大量重复代码没有敢重构.

2.随着项目的庞大和时间和流长,各种框架乱成麻:一是初始感觉良好的框架多半会出现严重的性能问题.二是可控性上会失控:控制不了细粒度实现,控制不了框架的完整性,会被来来去去的各种种色色的人搞得乱七八糟.三是新人进来会花费大量成本在框架学习和理解上,更要命的是学习前人破坏得面目全非的一个烂框架.

最终的解决方案就是写一个中庸的,真正面向对象的,现代的,半自动化的,半熟的程序结构.写好之后,任何项目,任何公司直接复制,通过简单的复制替换方法就能把底层物件全部搭建好.
下面以.NET为例.

三层基础结构: Entity,DataAccess,Components


Data.dll
数据帮助类:

using System;
using System.Data;
using System.Data.Common;
using System.Configuration;
using WQFree.Configuration;
using System.Data.OracleClient;

namespace WQFree.Data
{

    public class DataHelper
    {
        protected ConnectionStringSettings _connectionStrings;
        protected DbProviderFactory factory;

        protected bool autoClose = true;
        protected IDbConnection _connection = null;

        public bool AutoClose
        {
            get { return autoClose; }
            set { autoClose = value; }
        }

        public IDbConnection Connection
        {
            get
            {
                if (null == _connection)
                {
                    IDbConnection conn = factory.CreateConnection();
                    conn.ConnectionString = _connectionStrings.ConnectionString;
                    conn.Open();
                    _connection = conn;
                }
                else if (_connection.State != ConnectionState.Open)
                    _connection.Open();
                return _connection;
            }
            set
            {
                _connection = value;
            }
        }

        public ConnectionStringSettings ConnectionStrings
        {
            get { return _connectionStrings; }
            set { _connectionStrings = value; }
        }

        public DataHelper(string providerName, IDbConnection connection)
        {
            this._connection = connection;
            this._connectionStrings = new ConnectionStringSettings(providerName, connection.ConnectionString, providerName);
            Init();
        }

        public DataHelper(string providerName, IDbConnection connection, bool autoClose)
        {
            this.autoClose = autoClose;
            this._connection = connection;
            this._connectionStrings = new ConnectionStringSettings(providerName, connection.ConnectionString, providerName);
            Init();
        }

        public DataHelper(ConnectionStringSettings connectionStrings)
        {
            this._connectionStrings = connectionStrings;
            Init();
        }

        protected void Init()
        {
            if (null == factory)
            {

                factory = DbProviderFactories.GetFactory(_connectionStrings.ProviderName);
            }
        }

        public IDataReader ExcuteDataReader(string cmdText, CommandType cmdType, params IDataParameter[] parameters)
        {
            IDbConnection conn = Connection;
            DbCommand cmd = conn.CreateCommand() as DbCommand;
            cmd.CommandText = cmdText;
            cmd.CommandType = cmdType;
            cmd.Parameters.AddRange(parameters);
            if (!autoClose)
                return cmd.ExecuteReader(CommandBehavior.CloseConnection);
            return cmd.ExecuteReader();
        }

        public object ExecuteScalar(string cmdText, CommandType cmdType, params IDataParameter[] parameters)
        {
            IDbConnection conn = Connection;
            DbCommand cmd = conn.CreateCommand() as DbCommand;
            cmd.CommandText = cmdText;
            cmd.CommandType = cmdType;
            cmd.Parameters.AddRange(parameters);
            object o = cmd.ExecuteScalar();
            if (autoClose) conn.Close();
            return o;
        }

        public DataSet ExecuteDataSet(string cmdText, CommandType cmdType, string tableName, params IDataParameter[] parameters)
        {
            IDbConnection conn = Connection;
            DbCommand cmd = conn.CreateCommand() as DbCommand;
            cmd.Parameters.AddRange(parameters);
            cmd.CommandText = cmdText;
            cmd.CommandType = cmdType;
            DbDataAdapter da = factory.CreateDataAdapter();
            da.SelectCommand = cmd;
            DataSet ds = new DataSet(tableName);
            da.Fill(ds);
            if (autoClose) conn.Close();
            return ds;
        }

        public int Fill(DataSet ds, string cmdText, CommandType cmdType, params IDataParameter[] parameters)
        {
            IDbConnection conn = Connection;
            DbCommand cmd = conn.CreateCommand() as DbCommand;
            cmd.Parameters.AddRange(parameters);
            cmd.CommandText = cmdText;
            cmd.CommandType = cmdType;
            DbDataAdapter da = factory.CreateDataAdapter();
            da.SelectCommand = cmd;
            da.AcceptChangesDuringFill = false;

            int n = da.Fill(ds);
            if (autoClose) conn.Close();
            return n;
        }

        public int ExecuteNonQuery(string cmdText, CommandType cmdType, params IDataParameter[] parameters)
        {
            IDbConnection conn = Connection;
            DbCommand cmd = conn.CreateCommand() as DbCommand;
            cmd.CommandText = cmdText;
            cmd.CommandType = cmdType;
            cmd.Parameters.AddRange(parameters);
            int n = cmd.ExecuteNonQuery();
            if (autoClose) conn.Close();
            return n;
        }

        public virtual  IDbDataParameter CreateParameter(string name, object value)
        {
            DbParameter p = factory.CreateParameter();
            p.ParameterName = name;
            p.Value = value;
            return p;
        }

        public virtual IDbDataParameter CreateParameter(string name, DbType type, object value)
        {
            DbParameter p = factory.CreateParameter();
            p.ParameterName = name;
            p.DbType = type;
            p.Value = value;
            return p;
        }

        public virtual IDbDataParameter CreateParameter(string name, ParameterDirection direction, DbType type)
        {
            DbParameter p = null;
            p=factory.CreateParameter();
            p.DbType = type;
            p.Direction = direction;
            p.ParameterName = name;
            p.DbType = type;
            return p;
        }

        public virtual IDbDataParameter CreateParameter(string name, ParameterDirection direction, DbType type,int size)
        {
            DbParameter p = null;
            p = factory.CreateParameter();
            p.DbType = type;
            p.Direction = direction;
            p.ParameterName = name;
            p.DbType = type;
            p.Size = size;
            return p;
        }

        public virtual IDbDataParameter CreateParameter(string name, DbType type, int size, object value)
        {
            DbParameter p = factory.CreateParameter();
            p.ParameterName = name;
            p.Size = size;
            p.DbType = type;
            p.Value = value;
            return p;
        }

        public virtual IDbDataParameter CreateParameter(string name, DbType type, ParameterDirection direction, object value)
        {
            DbParameter p = factory.CreateParameter();
            p.ParameterName = name;
            p.DbType = type;
            p.Direction = direction;
            p.Value = value;
            return p;
        }

        public virtual IDbDataParameter CreateParameter(string name, DbType type, ParameterDirection direction, int size, object value)
        {
            DbParameter p = factory.CreateParameter();
            p.ParameterName = name;
            p.DbType = type;
            p.Direction = direction;
            p.Size = size;
            p.Value = value;
            return p;
        }

        

    }
}


数据访问对象基类:
using System;
using System.Data;
using System.Reflection;
using System.Configuration;
using System.Reflection.Emit;
using System.Collections.Generic;

namespace WQFree.Data
{
    public class DAO<T> : IDisposable  
    {
        protected DataHelper hp = null;
      
        public IDbConnection Connection
        {
            get
            {
                return hp.Connection;
            }
            set
            {
                hp.Connection = value;
            }
        }

        public bool AutoClose
        {
            get { return hp.AutoClose; }
            set { hp.AutoClose = value; }
        }

        public DAO() { hp = HelpFactory.Instance(); }

        public DAO(ConnectionStringSettings connectionStrings)
        {
            hp = HelpFactory.Instance(connectionStrings);
        }

        public DAO(string providerName, IDbConnection connection)
        {
            hp = HelpFactory.Instance(providerName, connection);
        }

        public DAO(string providerName, IDbConnection connection, bool mustClose)
        {
            hp = HelpFactory.Instance(providerName, connection, mustClose);
        }

        public List<T> BuildList(DataTable dt)
        {
            return EntityBuilder<T>.BuildList(dt);
        }

        public T Build(DataTable dt)
        {
            return EntityBuilder<T>.Build(dt);
        }

        public T Build(DataRow row)
        {
            return EntityBuilder<T>.Build(row);
        }


        public List<T> BuildList(IDataReader dr)
        {
            return EntityBuilder<T>.BuildList(dr);
        }

        public T Build(IDataReader dr)
        {
            return EntityBuilder<T>.Build(dr);
        }

        public void Close()
        {
            IDbConnection conn = Connection;
            if (conn != null && conn.State == ConnectionState.Open)
                conn.Close();
        }


        #region IDisposable 成员

        public void Dispose()
        {
            Close();
        }

        #endregion
    }
}


高性能实体映射类:
using System;
using System.Data;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

namespace WQFree.Data
{
    public class EntityBuilder<T>
    {
        private static readonly MethodInfo getValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(int) });
        private static readonly MethodInfo isNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(int) });
        private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) });
        private static readonly MethodInfo getRecordValueMethod = typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) });
        private delegate T LoadDataRow(DataRow dataRow);
        private LoadDataRow dataRowHandler;
        private delegate T LoadRecord(IDataRecord dataRecord);
        private LoadRecord recordHander;

        private static Dictionary<string, EntityBuilder<T>> builders = new Dictionary<string, EntityBuilder<T>>();
        
        public T BuildEntity(DataRow dataRow)
        {
            return dataRowHandler(dataRow);
        }

        public T BuildEntity(IDataRecord dataRecord)
        {
            return recordHander(dataRecord);
        }

        private static EntityBuilder<T> CreateBuilder(DataRow dataRow)
        {
            Type type = typeof(T);
            string key="Row:"+type.Name;
            if (builders.ContainsKey(key))
                return builders[key];
            EntityBuilder<T> dynamicBuilder = new EntityBuilder<T>();
            DynamicMethod method = new DynamicMethod("DataRowCreateT", type, new Type[] { typeof(DataRow) }, typeof(T), true);
            ILGenerator generator = method.GetILGenerator();
            LocalBuilder result = generator.DeclareLocal(type);
            generator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
            generator.Emit(OpCodes.Stloc, result);
            int len = dataRow.ItemArray.Length;
            for (int i = 0; i < len; i++)
            {
                System.Reflection.PropertyInfo propertyInfo = type.GetProperty(dataRow.Table.Columns[i].ColumnName);
                Label endIfLabel = generator.DefineLabel();
                if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
                {
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, isNullMethod);
                    generator.Emit(OpCodes.Brtrue, endIfLabel);
                    generator.Emit(OpCodes.Ldloc, result);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, getValueMethod);
                    generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                    generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                    generator.MarkLabel(endIfLabel);
                }
            }
            generator.Emit(OpCodes.Ldloc, result);
            generator.Emit(OpCodes.Ret);
            dynamicBuilder.dataRowHandler = (LoadDataRow)method.CreateDelegate(typeof(LoadDataRow));
            builders.Add(key, dynamicBuilder);
            return dynamicBuilder;
        }

        private static EntityBuilder<T> CreateBuilder(IDataRecord dataRecord)
        {
            Type type = typeof(T);
            string key = "Record:" + type.Name;
            if (builders.ContainsKey(key))
                return builders[key];
            EntityBuilder<T> dynamicBuilder = new EntityBuilder<T>();
            DynamicMethod method = new DynamicMethod("DataRecordCreateT", type, new Type[] { typeof(IDataRecord) }, typeof(T), true);
            ILGenerator generator = method.GetILGenerator();
            LocalBuilder result = generator.DeclareLocal(type);
            generator.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
            generator.Emit(OpCodes.Stloc, result);
            int len = dataRecord.FieldCount - 1;
            for (int i = 0; i < len; i++)
            {
                PropertyInfo propertyInfo = type.GetProperty(dataRecord.GetName(i));
                Label endIfLabel = generator.DefineLabel();
                if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
                {
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, isDBNullMethod);
                    generator.Emit(OpCodes.Brtrue, endIfLabel);
                    generator.Emit(OpCodes.Ldloc, result);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    generator.Emit(OpCodes.Callvirt, getRecordValueMethod);
                    generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
                    // generator.Emit(OpCodes.Unbox_Any, dataRecord.GetFieldType(i));
                    generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                    generator.MarkLabel(endIfLabel);
                }
            }
            generator.Emit(OpCodes.Ldloc, result);
            generator.Emit(OpCodes.Ret);
            dynamicBuilder.recordHander = (LoadRecord)method.CreateDelegate(typeof(LoadRecord));
            builders.Add(key, dynamicBuilder);
            return dynamicBuilder;
        }

        public static List<T> BuildList(DataTable dt)
        {
            List<T> list = new List<T>();
            if (dt == null || dt.Rows.Count==0) return list;
            EntityBuilder<T> builder = EntityBuilder<T>.CreateBuilder(dt.Rows[0]);
            foreach (DataRow info in dt.Rows)
                list.Add(builder.BuildEntity(info));
            dt.Dispose(); dt = null;
            return list;
        }

        public static T Build(DataRow row)
        {
            EntityBuilder<T> builder = CreateBuilder(row);
            return builder.BuildEntity(row);
        }

        public static T Build(DataTable dt)
        {
            EntityBuilder<T> builder = CreateBuilder(dt.Rows[0]);
            return builder.BuildEntity(dt.Rows[0]);
        }

        public static List<T> BuildList(IDataReader dr)
        {
            EntityBuilder<T> builder = CreateBuilder(dr);
            List<T> list = new List<T>();
            if (dr == null) return list;
            while (dr.Read())
                list.Add(builder.BuildEntity(dr));
            return list;
        }

        public static T Build(IDataReader dr)
        {
            EntityBuilder<T> builder = CreateBuilder(dr);
            if (dr.Read())
                return builder.BuildEntity(dr);
            return default(T);
        }

    }
}


DataAccess.dll
具体实现类:

 public class UserDao : DAO<User>
    {
        const string saveSP = "PKG_WQFree.UserSaveSP";

        public int Save(User entity)
        {
            IDbDataParameter[] pars = new IDbDataParameter[]
            {
                hp.CreateParameter("InEntityId",entity.EntityId), 
                hp.CreateParameter("InUserName",entity.UserName)
            };                     
            return hp.ExecuteNonQuery(saveSP, CommandType.StoredProcedure, pars);
        }

        public int Save(List<User> entities)
        {
            int ret = 0;
            this.AutoClose = false;
            using (IDbConnection conn = Connection)
            {
                foreach (User art in entities)
                {
                    ret += Save(art);
                }
            }
            return ret;
        }

        public int Save(DataTable datas)
        {
            List<User> entities = this.BuildList(datas);
            return Save(entities);
        }
     }


Components.dll
组件层:
 public class UserMgr
    {
        UserDao dao = new UserDao();

        public int Save(User entity)
        {
            try
            {
                return dao.Save(entity);
            }
            catch (Exception e)
            {
                Log.Error("保存User失败:" + e.Message);
            }
            return -1;
        }

        public List<User> BuildList(DataTable dt)
        {
            try
            {

                return dao.BuildList(dt);
            }
            catch (Exception e)
            {
                Log.Error("获取User列表失败:" + e.Message);
            }
            return new List<User>();
        }

        public int Save(DataTable datas)
        {
            try
            {
                return dao.Save(datas);
            }
            catch (Exception e)
            {
                Log.Error("保存User失败:" + e.Message);
            }
            return -1;
        }
    }

上面几层次的基本实现,严格按照结构,格式和风格来写,不要做任何改变,不要加入任何业务逻辑:可用复制替换的方法快速开发完毕,就是一个高效可完全控制核心代码的ORM.

上面的实现中留给读者两个小作业题自行解决:
1. 兼容Oracle 和Sql数据库,可以通过DataHelperFactory来实现,怎么解决OracleType和DbType之间的映射问题?
2. 在"实体映射类"中怎么样解决Oracle和SQL对应实体属性名称大小写问题?


请期待下一节:完美开发模型之贫血模型参考(II):Service/Facade/UI层

请期待下一篇:完美开发模型之领域模型参考

如有疑问请发: wopani@gmail.com

原文地址:https://www.cnblogs.com/kcitwm/p/2047331.html