学习之路二十九:泛型和委托在重构中的运用

最近在对项目中的代码进行重构,在重构的过程中发现了一些很有趣的东西,就是泛型和委托!

泛型和委托给我的重构带来了很大的便利,我也对它们在重构的技术上有了一些新的认识!

如果小菜说的不对,还请园友们帮我指出,我会虚心接受的,谢谢~!

下面我只是说泛型和委托在重构时的一些观点,不包含泛型和委托的所有知识点!

一丶泛型

重构前代码:

public class Test
    {
        public object TestOne(int number)
        {
            switch (number) //根据传过来的number来选择对象
            {
                case 1:
                    return Test1();
                case 2:
                    return Test2();
                default:
                    return null;
            }
        }

        public Person1 Test1() //很简单,返回一个对象实例
        {
            Person1 person1 = new Person1();
            return person1;
        }

        public Person2 Test2() //这个也是返回一个对象实例
        {
            Person2 person2 = new Person2();
            return person2;
        }
    }

    public class Person1
    { }

    public class Person2
    { }

分析:

①首先看到有两个方法的操作行为都是返回对象实例!

②也就是操作行为是相似的,但是返回的类型是不一样的!

③找到了相同点之后,我们就要对经常变化的点进行封装了,也就是我们要封装类型的变化点!

③最后我们很容易想到用泛型实现这样的需求!

重构后代码:

public class Test
    {
        public object TestOne(int number)
        {
            switch (number)
            {
                case 1:
                    return TestTwo<Person1>();
                case 2:
                    return TestTwo<Person2>();
                default:
                    return null;
            }
        }

        public T TestTwo<T>() where T : class,new() //通过泛型来封装类型变化点
        {
            T t = new T();
            return t;
        }
    }

    public class Person1
    { }

    public class Person2
    { }

结论:泛型封装类型变化点

通过泛型来传递类型,规定现在我们应该使用哪一种类型,这样就可以做到类型封装,从而做到以不变应万变!

二丶委托

重构前代码:

public class Test
{
    public void Test1()
    {
        DataTable table = new DataTable(); //获取数据源
        //生成Entity
        foreach (DataRow item in table.Rows)
        {
            GenerateEntity1(item);
        }
    }

    public void Test2()
    {
        DataTable table = new DataTable(); //获取数据源
        //生成Entity
        foreach (DataRow item in table.Rows)
        {
            GenerateEntity2(item);
        }
    }

    public void GenerateEntity1(DataRow dataRow)
    {
        //生成Entity1
    }

    public void GenerateEntity2(DataRow dataRow)
    {
        //生成Entity2
    }
}

分析:

①首先会看到Test1Test2方法中在生成Entity时会有foreach循环的重复代码!

②要想提取出两边的通用代码,需要分析两边哪些代码是一样的,哪些是变化的,这样我们就能提取出不变的代码,而用一些技术来封装变化点,最终做到通用!

③我们很容易发现foreach循环的代码是一样的,生成entity是不一样的,但是在仔细看一下生成entity方法的参数和返回值是一样的!

④那么怎么来封装那个经常变化的点呢?

⑤我们想到委托,通过委托来传递操作行为来做到变化!

重构后代码:

public class Test2
{
    public void Test1()
    {
        DataTable table = new DataTable(); //数据源
        GenerateEntity(table, GenerateEntity1);
    }

    public void Test2()
    {
        DataTable table = new DataTable();  //数据源,跟上面不同的数据源
        GenerateEntity(table, GenerateEntity2);
    }

    public void GenerateEntity1(DataRow dataRow)
    {
        //生成Entity1
    }

    public void GenerateEntity2(DataRow dataRow)
    {
        //生成Entity2
    }

    private void GenerateEntity(DataTable table, Action<DataRow> action)  //通过委托来实现操作行为的传递
    {
        foreach (DataRow item in table.Rows)
        {
            action(item);
        }
    }
}

结论:委托封装行为操作变化点

通过传递委托实例来指定我需要使用那种操作行为,如果发现返回值不一样,那么再加上泛型来封装类型的变化点,最后就构成了委托和泛型的综合利用!

三丶总结

其实就是想说在实践中进行思考,进行总结,最终一定会有不一样的收获!


学习之路三十三:重构技巧的学习

最近看了圣殿骑士大哥的重构文章,其中有几个重构技巧让我颇有感触,特此记录下。

文章地址:31天重构学习笔记重新整理下载

1.封装集合,返回集合接口类型

这个技巧让我想起了项目中的代码,比如:

public class Check
{
    private List<Detail> _details = new List<Detail>();

    public IList<Detail> GetDetails()
    {
        return _details;
    }
}

public class Detail
{
    public string Name { get; set; }
}

如果这样设计,那么用户通过调用GetDetails方法之后就可以对你的集合做任意的改动,这样的做法是非常危险的,所以必须要改进。

重构原则:返回可迭代器类型接口来保证对集合的封装,改进如下:

public class Check
{
    private List<Detail> _details = new List<Detail>();

    private Dictionary<int, Detail> _specificDetail = new Dictionary<int, Detail>();

    public IEnumerable<Detail> GetDetails()
    {
        return _details;
    }

    public IEnumerable<KeyValuePair<int, Detail>> GetSpecificDetail()
    {
        return _specificDetail;
    }
}

public class Detail
{
    public string Name { get; set; }
}

迭代器文章入口:迭代器学习之一:使用IEnumerableIEnumerator接口

我将在项目中运用此重构技巧。

2.提取判断条件作为方法 - 方法名要有意义

当遇到复杂的判断后,如果没有注释,很多人第一眼都不会理解这个判断是做什么的。

所以把复杂的判断条件提取为方法,再取个有意义的名字,那么别人看一眼就明白做什么的,比如:

public class Check
{
    public Detail GetDetail { get; set; }

    public void SendMessage()
    {
        //这样的判断别人根本不知道是干什么的
        if (GetDetail != null && GetDetail.Number > 0)
        {
            //Send Message
        }

        //重构后的代码
        //这样别人就知道这个判断的作用:判断当前的信息有没有提交
        if (HasConfirm(GetDetail))
        {
            //Send Message
        }
    }

    public bool HasConfirm(Detail detail)
    {
        return GetDetail != null && GetDetail.Number > 0;
    }
}

public class Detail
{
    public int Number { get; set; }
}

3.为带有大量bool参数的方法重新进行拆分组合

这个重构手法在项目中还没有用过,方法带有大量的bool参数本来就很奇葩,不过如果真的遇到了,我们也有应对方法,如下:

public class Test
{
    //如果是这样的一个方法,你知道它是干什么的吗,根本就不能理解它的用意
    private void Create(bool isA, bool isB, bool isC)
    { }
}

重构后的代码:

public class Test
{
    //进行拆分后在重新组合
    public void CreateA()
    {
        this.Create(true, false, false);
    }

    public void CreateB()
    {
        this.Create(false, true, false);
    }

    public void CreateC()
    {
        this.Create(false, false, true);
    }

    private void Create(bool isA, bool isB, bool isC)
    { }
}

4.避免双重否定

其实这个重构手法我理解为尽量避免使用在if条件里使用 - “!”.

因为如果本来你的判断条件就是表达否定的意思,那么在加上一个否定的判断,那么就会是双重否定。

那别人理解起来是不是会很抓狂,他会在心理画个圈圈狠狠的诅咒你的,😃,比如:

public class Test
{
    public void TestOne()
    {
        //本来想表达已经付款了,所以只能加个否定来判断咯
        //可是这个交别人理解起来真的很抓狂
        if (!NoPayment)
        {
            //TO DO
        }
    }

    public bool NoPayment { get; set; }
}

重构后的代码:

public class Test
{
    public void TestOne()
    {
        //本来想表达已经付款了,所以只能加个否定来判断咯
        //可是这个交别人理解起来真的很抓狂
        if (!NoPayment)
        {
            //TO DO
        }

        //重构后的代码
        //避免使用双重否定来作为判断条件
        if (HasPayment)
        {

        }
    }

    public bool NoPayment { get; set; }

    public bool HasPayment { get; set; }
}

5.尽快返回 - 返回值

这个重构技巧可以分解复杂的判断条件,是那些冗余的判断分支化成简介的小分支,比如:

public class TestOne
{
    private List<Detail> _details;

    public void Display()
    {
        //这么一大长串的判断条件是不是很恶心
        if (_details != null)
        {
            if (_details.Count > 0)
            {
                foreach (var item in _details)
                {
                    if (item.Number > 0)
                    {
                        //TO DO
                    }
                }
            }
        }
    }
}

public class Detail
{
    public int Number { get; set; }
}

重构后的代码:

public class TestOne
{
    private List<Detail> _details;

    public void Display()
    {
        //重构之后是不是很清爽呀
        //哈哈
        if (_details == null || _details.Count <= 0)
            return;
        foreach (var item in _details.Where(p => p.Number > 0).Select(p => p))
        {
            //TO DO
        }
    }
}

public class Detail
{
    public int Number { get; set; }
}

学习之路四十一丶简论重构

尤其随着代码量越来越大,逻辑越来越复杂,代码的清晰,健壮,扩展性成了一个需要重视的问题,也就是要适时的重构了。

一丶重构的时机

上个星期在修改一块重大逻辑的时候,需要修改很多代码,当时我犯了一个错误,一开始想了一个思路,但一上来没写多少就开始想着重构代码,目的是使其代码清晰以及可扩展。

可是随着时间的流失,不仅没有重构好,而且该改的逻辑也没有改好,我很郁闷,为什么会这样呢?于是开始了沉思 - 重构的时机应该是什么时候?

重构的时机要把握的恰到好处,太早缺乏代码之间的连贯性,因为你代码都没有写好,就想着开始重构了,那可不好;太晚代码写好了,逻辑也通了,但是改起来特别费力,是牵一发而动全身,说不定还改出了更多问题呢,需要花更多的时间去修改代码以及 fix bug。

所以重构的时机因人而异,因你的能力,因你的业务流程熟悉度,不过我选择的时机是中间靠前一点。

因为这个时候你已经写了将近一半的代码,业务逻辑也已经实现了一半了,那么是否出现代码冗余,阅读性差,抽象程度差的问题,如果有的话就要想想该怎么重构了,如果没有继续往下写,保持好状态。

当快要接近完成的时候,再来看看以前的代码,因为从现在的眼光看以前写的,总会有新的想法,如果有,赶紧实施,写出高质量代码。

所以重构的时机要自己掌握好,其实有几点可以表明你需要重构:

  1. 冗余,重复代码太多;
  2. 逻辑层次不清晰,变量命名自己都搞不清楚了;
  3. 类,方法各个层次之间很混乱;
  4. 无法进行有效的扩展;
  5. 健壮性太差

所以在遇到上面一些问题之后,就要思考需要用哪些重构技巧来改善代码质量了,这个需要积累,加油。

二丶重构的一些技巧

这里就写一些我重构用的方法:

  1. 去重,提取共用方法
    记得写过一个去除重复的try-catch的代码,用到了委托以及反射来减少重复代码

2.抽象
接口,继承,虚方法,抽象类。
其实最重要的是你要深刻理解你的业务,并且抽取他们共同的部分,以及通过虚方法来实现一个方法的不同操作

  1. 抽象工厂
    抽象工厂又可以分为:单例工厂和多实例工厂

  2. 配置文件
    自定义配置文件,可以更有效的控制不同方式做不同的事

  3. 灵活运用Attribute

  4. 取更有意义的变量,属性,方法,类的名称,长一点没有什么大碍

  5. 反射

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值