[重构]Feature Envy

Feature Envy(依恋情结)

官方的:函数对某个类的兴趣高过对自己所处类的兴趣。

影响:数据和行为不在一处,修改不可控。

目标:将数据和操作数据的行为包装在一起。


Demo:媒婆、一小伙、一小姑娘

场景:函数的全部数据都来自于另外一个类

媒婆、小伙先出场

public class Boy
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string Adress { get; set; }
        public bool IsSinglehood { get; set; }
        public bool IsHightRichHandsome { get; set; }
    }

    public class WomanMatchMaker
    {
        private readonly Boy boy;
        ...
        public string GetBoyInformation()
        {
            return string.Format("Name: {0} Age: {1} Adress: {2} the boys is {3} and he is {4}", 
                boy.Name, 
                boy.Age, 
                boy.Adress, 
                (boy.IsSinglehood ? "Singlehood " : "married"), 
                (boy.IsHightRichHandsome ? "HightRichHandsome" : "DiaoSi"));
        }
    }

从上面代码看出:GetBoyInformation函数中所有数据都是从Boy类中获取。如果Boy中的属性发生变化,就要改WomanMatchMaker类,修改多处。为了让数据和行为变化在一处,Boy应该负责提供本人信息的这种行为。


public class Boy
    {
        ...
        public string GetInformation()
        {
            return string.Format("Name: {0} Age: {1} Adress: {2} the boys is {3} and he is {4}", 
                Name, 
                Age, 
                Adress, 
                (IsSinglehood ? "Singlehood " : "married"), 
                (IsHightRichHandsome ? "HightRichHandsome" : "DiaoSi"));
        }
    }

    public class WomanMatchmaker
    {
        private readonly Boy boy;
        ...
        public string GetBoyInformation()
        {
            return boy.GetInformation();
        }
    }

将GetBoyInformation函数Move到Boy类中。然后WomanMatchMarker中用一委托。

场景二:函数有一部分数据来自于另一个类。

现在需求稍微有点变动。需要提供男孩信息的同时要提供媒婆信息。

public class WomanMatchmaker
    {
        public string Name { get; set; }
        public string Adress { get; set; }

        private readonly Boy boy;
        ...
        public string GetBoyInformationWithComeFromInformation()
        {
            return string.Format("Name: {0} Age: {1} Adress: {2} the boys is {3} and he is {4}, {5}",
                boy.Name, 
                boy.Age, 
                boy.Adress, 
                (boy.IsSinglehood ? "Singlehood " : "married"), 
                (boy.IsHightRichHandsome ? "HightRichHandsome" : "DiaoSi"), 
                GetInformationComeFrom());
        }        

        private string GetInformationComeFrom()
        {
            return string.Format("Information comes from: {0} Adress: {1}", Name, Adress);
        }
    }

此函数中有部分使用另一个类的数据。同第一种做法稍有不同,先把从Boy中来的数据Extract 到一个独立函数中,在把它移到它想去的地方。那么媒婆类就变成这个样子了。

public class WomanMatchMaker
    {
        public string Name { get; set; }
        public string Adress { get; set; }

        private readonly Boy boy;
        ...
        public string GetBoyInformationWithComeFromInformation()
        {
            return boy.GetInformation() + GetInformationComeFrom();
        }

        private string GetInformationComeFrom()
        {
            return string.Format("Information comes from: {0} Adress: {1}", Name, Adress);
        }
    }

场景三:函数使用多个类的数据

等了这么久,姑娘终于出场了。媒婆要看看小伙子和姑娘是否合适。

有了上面的两个场景。再来看看这个方法有没有Refactor的Sense。

public string MatchedPairResult()
        {
            //boy information
            var boyInfo = string.Format("Name: {0} Age: {1} Adress: {2}...", boy.Name, boy.Age, boy.Adress);
            //girl information
            var girlInfo = string.Format("Name: {0} Age: {1} Adress: {2}...", girl.Name, girl.Age, girl.Adress);

            return (RuleMatching(boyInfo, girlInfo) ? "To be together!" : "Improper!") + GetInformationComeFrom();
        }

这个函数用到了多个类的数据。那么我们就分别把它们提炼到独立函数中,在分别送它们想去的地方。

public class WomanMatchMaker
    {
        ...
        public string MatchedPairResult()
        {
            //boy information
            var boyInfo = boy.GetInformation();
            //girl information
            var girlInfo = girl.GetInformation();

            return (RuleMatching(boyInfo, girlInfo) ? "To be together!" : "Improper!") + GetInformationComeFrom();
        }

        private bool RuleMatching(string boyInfo, string girlInfo)
        {
            throw new NotImplementedException();
        }
        ...
    }

经过一番重构之后,他们终于幸福的在一起了。

总结:

1、函数全部数据来自另外一个类

      做法:将数据提炼到一个独立函数中  Move method。

2、函数部分数据来自另外一个类

      做法:将“部分数据”提炼到一个函数中 Move method。

3、函数的数据来自不同类

      做法:将数据分类,分别提炼各自的独立的函数,在将这些函数移到各自属于的类中。


PS:Refactoring书中提到过这种。如果一个函数使用多个类的数据,那么要判断哪个类拥有最多此函数使用的数据,这个函数应该被移到哪,这种场景是在是没想到过。如果这个函数用到多个类的数据。分别提炼函数,在移到相应的类中不就行了。个人认为,函数应该属于某个对象的职责,应该根据情况而定。


2012/8/19



展开阅读全文

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