【问题标题】:Refactoring methods that use the same code but different methods inside重构使用相同代码但内部方法不同的方法
【发布时间】:2015-12-30 11:01:58
【问题描述】:

除了一行之外,我有一些相同的方法(我在对象客户端上调用不同的方法)。 我会有更多这样的方法。

除了使用 Reflection 之外,还有其他解决方案吗?

 private void initClerks(Client client)
            {
                string[] pks = client.ClerksPKS.Trim(','). Split(',');

                foreach (string pk in pks)
                {
                    string data = JObject.Parse(DBUtils.GetData(Constants.DBProcedures.GetProcedures.GetWorkerDetailsByPkid, pk))[Constants.ResponseJson.Data].ToString();

                    client.addClerk(JsonConvert.DeserializeObject<Clerk[]>(data)[0]);

                }

            }


private void initManagers(Client client)
            {
                string[] pks = client.ManagerPK.Trim(',').Split(',');



                foreach (string pk in pks)
                {
                    string data = JObject.Parse(DBUtils.GetData(Constants.DBProcedures.GetProcedures.GetWorkerDetailsByPkid, pk))[Constants.ResponseJson.Data].ToString();
                    client.addManager(JsonConvert.DeserializeObject<Manager[]>(data)[0]);

                }

            }

【问题讨论】:

  • 你解析JSON字符串,然后再转回字符串,然后反序列化...
  • "除了一行之外是相同的(我在对象客户端上调用不同的方法)" - 我计算了三个差异 - pks 是如何初始化的,是的,client 方法,还有DeserializeObject 的类型参数。
  • 嗯,对我来说,这似乎更像是解决另一个问题的方法,即存储主键(pks?)和获取数据的方式。您也许可以增强这些以更好地控制初始化过程。

标签: c# asp.net


【解决方案1】:

你可以通过将一个动作传递给一个方法来做到这一点。类似的东西

private void actOnData(Client client, string[] pks, Action<Client, string> addThing)
{
    foreach (string pk in pks)
    {
        string data = JObject.Parse(DBUtils.GetData(Constants.DBProcedures.GetProcedures.GetWorkerDetailsByPkid, pk))[Constants.ResponseJson.Data].ToString();
        addThing(client, data);
    }
}

private void initClerks(Client client)
{
    string[] pks = client.ClerksPKS.Trim(',').Split(',');
    actOnData(client,pks,(c,d) => { c.addClerk(JsonConvert.DeserializeObject<Clerk[]>(d)[0]); });
}

private void initManagers(Client client)
{
    string[] pks = client.ManagerPK.Trim(',').Split(',');
    actOnData(client, pks, (c, d) => { c.addManager(JsonConvert.DeserializeObject<Manager[]>(d)[0]); });
}

【讨论】:

  • 我喜欢你的解决方案,这正是我一直在寻找的。​​span>
【解决方案2】:

考虑到这不是外部遗留库,我建议重构 Client 类以简化其 API(我会进行更多更改,但我们会在某个时候停下来)

伪代码:

// taking into account Client, manager all are workers
class Client
{
    // further whenever you need filter out managers use LINQ OfType<>
    List<Workers> workers;

    public void Add<T>(T worker) where T: Worker
    {
        workers.Add(client);
    }
}

参见Extract method 方法,

private void Initialize<T>(Client client, string[] pks)
{
    foreach (string pk in pks)
    {
        string data = JObject.Parse(DBUtils.GetData(Constants.DBProcedures.GetProcedures.GetWorkerDetailsByPkid, pk))[Constants.ResponseJson.Data].ToString();
        client.Add(JsonConvert.DeserializeObject<T[]>(data)[0]);    
    }
}

private void initClerks(Client client)
{
    string[] pks = client.ClerksPKS.Trim(',').Split(',');
    Initialize<Clerk>(client, pks);
}

private void initManagers(Client client)
{
    string[] pks = client.ManagerPK.Trim(',').Split(',');
    Initialize<Manager>(client, pks);           
}

接下来,这两个 intiClerks/initmanagers 看起来是多余的,只需内联调用 Initialize(当然,如果整个代码库不比您在此处显示的更复杂)

【讨论】:

  • 他正在调用 addClerk 和 addManager,所以不能只提取 foreach。
  • 还有DeserializeObject&lt;Clerk[]&gt; vs DeserializeObject&lt;Manager[]&gt;。正如我在对该问题的评论中所说,我计算了 三个 差异而不是一个。
  • 哦,伙计错过了,我很抱歉。添加了泛型类型参数,addManager() 调用的另一个问题将解决此问题
  • @sll,您仍然拥有 addManager,并且应该将 addClerk 重构为重载方法以使其正常工作。
  • NP,所有这些问题都来自错误的设计决策。我看到你所有的代码都需要一些ID(pks)形成一个对象,然后创建一组新对象并将它们添加到同一个客户端,这可能会进一步简化,考虑单一责任原则并将数据实体搞乱业务逻辑,然后您找到一种方法来返工解决方案,所有其他解决方法,例如传递复杂的操作或添加更多 AROUND 最初错误的设计会使事情进一步复杂化,您应该尽可能不要解决方法
【解决方案3】:

提取方法的典型路径是查找差异并声明相应的方法参数。让你的initClerks方法在里面找到Clerk这个词。其中有 3 个,一个是Type,所以我们将创建一个泛型方法,泛型参数T 对应于Clerk。映射将是这样的

(1) client.ClerksPKS 映射到 Func&lt;Client, string&gt;
(2) JsonConvert.DeserializeObject&lt;Clerk[]&gt; 映射到 JsonConvert.DeserializeObject&lt;T[]&gt;
(3)client.addClerk映射到Action&lt;Client, T&gt;

所以常用的方法就变成了

void Init<T>(Client client, Func<Client, string> getPKS, Action<Client, T> addItem)
{
    string[] pks = getPKS(client).Trim(','). Split(',');
    foreach (string pk in pks)
    {
        string data = JObject.Parse(DBUtils.GetData(Constants.DBProcedures.GetProcedures.GetWorkerDetailsByPkid, pk))[Constants.ResponseJson.Data].ToString();
        addItem(client, JsonConvert.DeserializeObject<T[]>(data)[0]);
    }
}

及用途

private void initClerks(Client client)
{
    Init<Clerk>(client, c => c.ClerksPKS, (c, x) => c.addClerk(x));
}

private void initManagers(Client client)
{
    Init<Manager>(client, c => c.ManagerPK, (c, x) => c.addManager(x));
}

【讨论】:

    【解决方案4】:

    创建基类以在基类Init方法中添加通用逻辑。 然后在实现 BaseClientInitializer 的类中重写 Insert 方法。这种设计模式称为模板方法。

    public abstract class BaseClientInitializer
      {
        private string[] keys;
        public BaseClientInitializer(string[] keys)
        {
          this.keys = keys;
        }
    public abstract void Insert(string data);
    
    public void Init()
    {
      foreach (string pk in keys)
      {
        var data = JObject.Parse(DBUtils.GetData(Constants.DBProcedures.GetProcedures.GetWorkerDetailsByPkid, pk))[
            Constants.ResponseJson.Data].ToString();
        this.Insert(data);
      }
    }   
    
    
     }
    
    public class Clerks : BaseClientInitializer
      {
        private Client client;
        public Clerks(Client client) : base(client.ClerksPKS.Trim(','). Split(','))
        {
          this.client = client;
        }
    
        public override void Insert(string data)
        {
          client.addClerk(JsonConvert.DeserializeObject<Clerk[]>(data)[0]);
        }
      }
    
    private void initClerks(Client client)
    {
         var clerksInitializer = new ClerksInitializer(client);
        clerksInitializer.Init();
    }
    

    对初始化管理器执行相同的操作。 不同之处不仅在于插入方法,pks 数组也不同:client.ClerksPKS.Trim(',')。 Split(',') - 用于文员,client.ManagerPK.Trim(',').Split(',') 用于经理。所以简单的提取方法将无法正常工作。

    【讨论】:

    • 或者只是简单的面向对象的方式将通用逻辑提取到可重用的小部分中;)
    • @sll 等到你看到产生ClerksInitializers 的ClerksInitializerFactory 和它派生自的BaseInitializerFactory
    • @Radin:每个问题都有特定的级别,解决方案的复杂程度应该大致相同,否则 OO 方式中的 Hello World 可能最终会出现在许多类和模式中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    • 2016-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多