重构笔记——提炼类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pistolove/article/details/43059759

本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/43059759



        在上一篇文章中介绍了搬移字段。本文将介绍“提炼类”这种重构手法。

        下面让我们来学习这种重构手法吧。



开门见山


        发现:某个类做了应该由两个类做的事。

        解决:建立一个新类,将相关的字段和函数从旧类搬移到新类。



动机


        我们或多或少听过这样的教诲:一个类应该是一个清楚的抽象,处理一些明确的责任。但是,在实际工作中,类会不断成长扩展。你会在这儿加一些功能,在那儿加入一些数据。 当给类添加新责任时,就会觉得不值得为这项责任分理出一个单独的类。于是,随着责任的不断增加,这个类会变得过分复杂。很快,类就会变成一团乱麻,继而你就越发不想管理。 
        这样的类往往含有大量函数和数据,往往太大而不易理解。此时,就需考虑哪些部分是可以分离出去的,并将它们分离到一个单独的类中。如果某些数据和某些函数总是一起出现,某些数据经常同时变化甚至彼此相依,这就说明应该将其分离处理。一个比较有用的测试就是问问自己:如果搬移了某些字段和函数,会发生什么事情?其它字段和函数是否会变得无意义? 
        还有一个值得注意的地方是类的子类化方式。如果发现子类化只影响类的部分特性,或如果发现某些特性需要以一种方式来子类化,而另一些特性则需要以另一种方式子类化,则意味着需要分解原来的类。 




做法


(1)决定如何分解类所负的责任。 
(2)建立一个新的类,用以表现从旧类中分离出来的责任(如果旧类剩余的责任与旧类名不符,则为旧类更名)。 
(3)建立从旧类访问新类的连接关系。(有可能需要一个双向连接,但是在真正需要它之前,不要建立“从新类通往旧类”的连接)
(4)对于待搬移的每个字段,运用“搬移字段”将其搬移。 
(5)每次搬移后,都进行编译、测试。 
(6)使用“搬移方法”将必要函数搬移到新类。先搬移较底层函数(即“被其它函数调用”多余“调用其它函数”的函数),然后搬移高层函数。 
(7)每次搬移后,都进行编译、测试。 
(8)检查,精简每个类的接口。(如果你建立了双向连接,检查是否应该将其改为单向连接) 
(9)决定是否公开新类,如果确定公开它,就要决定让它成为引用对象还是不可变对象。 



示例


        我们从一个简单的Person类开始:
	// 提炼类
	class Person {
		private String _name;
		private String _officeAreaCode;
		private String _officeNumber;

		public String get_name() {
			return _name;
		}

		public String getTelephoneNumber() {
			return ("(" + _officeAreaCode + ")" + _officeNumber);
		}

		public String get_officeAreaCode() {
			return _officeAreaCode;
		}

		public void set_officeAreaCode(String areaCode) {
			_officeAreaCode = areaCode;
		}

		public String get_officeNumber() {
			return _officeNumber;
		}

		public void set_officeNumber(String number) {
			_officeNumber = number;
		}
	}
        在这个例子中,可以将与电话号码相关的行为分离到一个独立类中。首先需要定义一个TelephoneNumber类来表示“电话号码”这个概念:
	class TelephoneNumber {

	}
        然后建立从Person到TelephoneNumber的连接:
	class Person {
		//...
		private TelephoneNumber _officeTelephone = new TelephoneNumber();
	}
       现在,可以运用“搬移字段”手法移动一个字段:
	class TelephoneNumber {
		private String _areaCode;

		public String get_AreaCode() {
			return _areaCode;
		}

		public void set_AreaCode(String areaCode) {
			_areaCode = areaCode;
		}
	}

	class Person {
		//......
		private TelephoneNumber _officeTelephone = new TelephoneNumber();

		public String getTelephoneNumber() {
			return ("(" + get_officeAreaCode() + ")" + _officeNumber);
		}

		public String get_officeAreaCode() {
			return _officeTelephone.get_AreaCode();
		}

		public void set_officeAreaCode(String areaCode) {
			_officeTelephone.set_AreaCode(areaCode);
		}
	}
        然后可以移动其它的字段,并运用“搬移函数”手法将相关函数移动到TelephoneNumber类中:
	class Person {
		//......
		private TelephoneNumber _officeTelephone = new TelephoneNumber();
		private String _name;

		public String get_name() {
			return _name;
		}

		public String getTelephoneNumber() {
			return _officeTelephone.getTelephoneNumber();
		}

		public TelephoneNumber getOfficeTelephone() {
			return _officeTelephone;
		}
	}

	class TelephoneNumber {
		private String _areaCode;
		private String _number;

		public String get_AreaCode() {
			return _areaCode;
		}

		public String getTelephoneNumber() {
			return ("(" + _areaCode + ")" + _number);
		}

		public void set_AreaCode(String areaCode) {
			_areaCode = areaCode;
		}

		public String getNumber() {
			return _number;
		}

		public void setNumber(String number) {
			_number = number;
		}
	}
        下一步要决定是否对用户公开这个新类?可以将Person中与电话号码相关的函数委托至TelephoneNumber,从而完全隐藏这个新类;也可以直接将它对用户公开。也可以将它公开给部分用户(位于同一个包中的用户),而不公开给其它用户。


        本文主要介绍了重构手法——提炼类。该重构手法在开发的过程中也会经常性地用到,想想平时在开发的过程中,将一个类中的某些特性移到另一个新的类中,将它们的职责分的清清楚楚,这样也便于开发可后期的维护。提炼类是改善并发程序的一种常用技术。因为它使得为提炼后的两个类可以分别加锁。但是如果不需要同是锁定两个对象,就不必这样做。这里也会面临事务的问题,当确保两个对象被同时锁定。这样也会存在一定的危险性。
        最后,希望本文对你有所帮助。有问题可以留言,谢谢。(PS:下一篇将介绍重构笔记——将类内联化)


重构笔记文章





如何引用重构的类?

03-05

下面是一个数据库访问的类,其中RunProc被多次重写,如何引用这个:rnpublic string RunProc(string procName, SqlParameter[] prams)rn(运行存储过程返回字符串)这个?rn++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++rnusing System;rnusing System.Data;rnusing System.Configuration;rnusing System.Web;rnusing System.Web.Security;rnusing System.Web.UI;rnusing System.Web.UI.WebControls;rnusing System.Web.UI.WebControls.WebParts;rnusing System.Web.UI.HtmlControls;rnusing System.Data.SqlClient;rnrn/// rn/// DataAccess 的摘要说明rn/// rnnamespace DALrnrn public class DataAccessrn rn private string ConnStr = null;rn public DataAccess()rn rn ConnStr = ConfigurationManager.AppSettings["connString"];rn rn public DataAccess(string Str)rn rn tryrn rn this.ConnStr = Str;rnrn rn catch (Exception ex)rn rn throw ex;rn rn rnrn /// rn /// 返回connection对象 rn /// rn /// rn public SqlConnection ReturnConn()rn rn SqlConnection Conn = new SqlConnection(ConnStr);rn Conn.Open();rn return Conn;rn rn public void Dispose(SqlConnection Conn)rn rn if (Conn != null)rn rn Conn.Close();rn Conn.Dispose();rn rn GC.Collect();rn rn /// rn /// 生成一个存储过程使用的sqlcommand. rn /// rn /// 存储过程名. rn /// 存储过程入参数组. rn /// sqlcommand对象. rn public SqlCommand CreateCmd(string procName, SqlParameter[] prams)rn rn SqlConnection Conn;rn Conn = new SqlConnection(ConnStr);rn Conn.Open();rn SqlCommand Cmd = new SqlCommand(procName, Conn);rn Cmd.CommandType = CommandType.StoredProcedure;rn if (prams != null)rn rn foreach (SqlParameter parameter in prams)rn rn if (parameter != null)rn rn Cmd.Parameters.Add(parameter);rn rn rn rn return Cmd;rn rnrn /// rn /// 为存储过程生成一个SqlCommand对象 rn /// rn /// 存储过程名 rn /// 存储过程参数 rn /// SqlCommand对象 rn private SqlCommand CreateCmd(string procName, SqlParameter[] prams, SqlDataReader Dr)rn rn SqlConnection Conn;rn Conn = new SqlConnection(ConnStr);rn Conn.Open();rn SqlCommand Cmd = new SqlCommand(procName, Conn);rn Cmd.CommandType = CommandType.StoredProcedure;rn if (prams != null)rn rn foreach (SqlParameter parameter in prams)rn Cmd.Parameters.Add(parameter);rn rn Cmd.Parameters.Add(rn new SqlParameter("ReturnValue", SqlDbType.Int, 4,rn ParameterDirection.ReturnValue, false, 0, 0,rn string.Empty, DataRowVersion.Default, null));rnrn return Cmd;rn rnrn /// rn /// 运行存储过程,返回. rn /// rn /// 存储过程名 rn /// 存储过程参数 rn /// SqlDataReader对象 rn public void RunProc(string procName, SqlParameter[] prams, SqlDataReader Dr)rn rnrn SqlCommand Cmd = CreateCmd(procName, prams, Dr);rn Dr = Cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);rn return;rn rnrn /// rn /// 运行存储过程,返回. rn /// rn /// 存储过程名 rn /// 存储过程参数 rn public string RunProc(string procName, SqlParameter[] prams)rn rn SqlDataReader Dr;rn SqlCommand Cmd = CreateCmd(procName, prams);rn Dr = Cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);rn if (Dr.Read())rn rn return Dr.GetValue(0).ToString();rn rn elsern rn return "";rn rn rnrn /// rn /// 运行存储过程,返回dataset. rn /// rn /// 存储过程名. rn /// 存储过程入参数组. rn /// dataset对象. rn public DataSet RunProc(string procName, SqlParameter[] prams, DataSet Ds)rn rn SqlCommand Cmd = CreateCmd(procName, prams);rn SqlDataAdapter Da = new SqlDataAdapter(Cmd);rn tryrn rn Da.Fill(Ds);rn rn catch (Exception Ex)rn rn throw Ex;rn rn return Ds;rn rnrn rn

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试