重構實踐——為了try-catch大興排場


可能是我們共同的強迫症,不要說看到,就算想到太多的try-catch也很難接受。

於是,開始了一些嘗試,這些嘗試都算是思維的鍛煉、場面的見識、經驗的積累。

Version1 —— 原始版本

一開始,在ConcreteService中,擁有了太多的try-catch,而影響碼字的興趣。

代碼1 原始代碼
/// <summary>
/// 契約
/// </summary>
public interface IUpdateManyManyThingContract
{
    bool UpdateSth1(DataSet ds);
    bool UpdateSth2(DataSet ds);
    bool UpdateSth3(DataSet ds);
    bool UpdateSth4(DataSet ds);
    //...
}

/// <summary>
/// 服務實現
/// </summary>
public class ConcreteService : IUpdateManyManyThingContract
{
    private IDao m_Dao;

    public bool UpdateSth1(DataSet ds)
    {
        try
        {
            var dt = ds.First();
            if (!dt.HasElements()) return true;

            foreach (DataRow row in dt.Rows)
            {
                //構造
                var entity = new Branch(row);
                m_Dao.SaveOrUpdate(entity);
            }
            return true;
        }
        catch (Exception ex)
        {
            Logger.Log(ex);
            return false;
        }
    }

    public bool UpdateSth2(DataSet ds)
    {
        try
        {
        }
        catch (Exception)
        {
        }
    }

    public bool UpdateSth3(DataSet ds)
    {
        throw new NotImplementedException();
    }

    public bool UpdateSth4(DataSet ds)
    {
        throw new NotImplementedException();
    }
    //many update methods,many try-catches...
}

如上代碼,UpdateSth函數里面都需要實現一個try-catch,而覺得惡心到自己了。

Version2——(Extract Method)提取方法 + Func

於是,基於自己的積累,開始了重構的第一個版本。

針對這個服務(ConcreteService)的特殊性,定制了一個專門的方法進行控制——TrycatchBlock

代碼2 提取方法片段
/// <summary>
/// 服務實現
/// </summary>
public class ConcreteService : IUpdateManyManyThingContract
{
    private IDao m_Dao;

    public bool UpdateSth1(DataSet ds)
    {
        return TrycatchBlock(() =>
        {
            var dt = ds.First();
            if (!dt.HasElements()) return true;

            foreach (DataRow row in dt.Rows)
            {
                //構造
                var entity = new Branch(row);
                m_Dao.SaveOrUpdate(entity);
            }
            return true;
        });
    }

    public bool UpdateSth2(DataSet ds)
    {
        return TrycatchBlock(() =>
        {
            //...
            return true;
            //...
            //return false;
        });
    }

    public bool UpdateSth3(DataSet ds)
    {
        throw new NotImplementedException();
    }

    public bool UpdateSth4(DataSet ds)
    {
        throw new NotImplementedException();
    }
    //many update methods,many try-catches...

    //try-catch控制塊
    private bool TrycatchBlock(Func<bool> body)
    {
        try
        {
            return body();
        }
        catch (Exception ex)
        {
            Logger.Log(ex);
            return false;
        }
    }
}

是的,這是一次進步,將所有的try-catch的功能職責都集中到了一個函數里面,也方便調試了。

但是,還得每個方法都加上一句:return TrycatchBlock(() => { 。。。 })。

從本質上來說,還是在進行中重復。

Version3——過濾器思想(否決)

經過老大的指點:考慮MVC中的類似FilterAttribute的注解。

 

思路演進:MVC中,有一個HandErrorAttribute的特性,用於攔截控制器或者動作的異常。。。。。。對,這是個思路,但過了沒多久,我就放棄了。

 

放棄理由:“Request請求——>路由數據——>ControllerInvoker——>反射調用Controller或Action。”,這里面用了很多元數據(***Descriptor,***Invoker等)手段,實現難度不小。

 

另外,我需要的是“instance.MethodAction”(對象直接調用方法)的方式,因為是為WCF直接提供服務(WCF會根據配置文件中服務的名稱創建服務),不需要使用反射進行動態調用。

 

Version4——動態代理

瀏覽網頁的過程中,想起動態代理——Castle Dynamic Proxy,是的,Moq,Spring.net等一系列優秀的框架中引用到了它。

V4.1.使用中間層

想起一個老外曾經說過的一句話“計算機的任何問題,都可以通過一個中間層來解決”,當然,這里的中間層,是一個廣泛和抽象的概念,比如,中間1調中間2中間2調目標,可能是一個遞歸的結構也說不定。

 

於是使用interceptor繼續一個版本:

代碼3:中間層——ConcreteServiceProxy;攔截器——ServiceDynamicProxyInterceptor。
/// <summary>
/// 服務實現
/// </summary>
public class ConcreteService : IUpdateManyManyThingContract
{
    private IDao m_Dao;

    public bool UpdateSth1(DataSet ds)
    {
        var dt = ds.First();
        if (!dt.HasElements()) return true;

        foreach (DataRow row in dt.Rows)
        {
            //構造
            var entity = new Branch(row);
            m_Dao.SaveOrUpdate(entity);
        }
        return true;
    }

    public bool UpdateSth2(DataSet ds)
    {
        //...
        return true;
        //...
        //return false;
    }

    public bool UpdateSth3(DataSet ds)
    {
        throw new NotImplementedException();
    }

    public bool UpdateSth4(DataSet ds)
    {
        throw new NotImplementedException();
    }
    //many update methods,many try-catches...
}

public class ConcreteServiceProxy : IUpdateManyManyThingContract
{
    private ConcreteService m_Service;
    public ConcreteServiceProxy()
    {
        m_Service = ServiceDynamicProxyInterceptor.CreateServiceProxy<ConcreteService>();
    }
    public bool UpdateSth1(DataSet ds)
    {
        return m_Service.UpdateSth1(ds);
    }

    public bool UpdateSth2(DataSet ds)
    {
        return m_Service.UpdateSth2(ds);
    }

    public bool UpdateSth3(DataSet ds)
    {
        return m_Service.UpdateSth3(ds);
    }

    public bool UpdateSth4(DataSet ds)
    {
        return m_Service.UpdateSth4(ds);
    }
}

public class ServiceDynamicProxyInterceptor : IInterceptor
{
    /// <summary>
    /// 工廠方法
    /// </summary>
    /// <typeparam name="T">服務類型</typeparam>
    /// <returns>一個經過代理的服務</returns>
    public static T CreateServiceProxy<T>() where T : class
    {
        ProxyGenerator generator = new ProxyGenerator();
        ServiceDynamicProxyInterceptor interceptor = new ServiceDynamicProxyInterceptor();
        T entity = generator.CreateClassProxy<T>(interceptor);
        return entity;
    }

    public void Intercept(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
        }
        catch (Exception ex)
        {
            Log.Error(ex.Message);
            invocation.ReturnValue = false;
        }
    }
}

上述代碼是一目了然,使用m_Service = ServiceDynamicProxyInterceptor.CreateServiceProxy<ConcreteService>();就得到一個代理過的對象,也就能夠進行攔截

 

多了一個中間層——ConcreteServiceProxy,層次分明了,但是代碼同樣沒有減少,這個似乎又看起來多次一舉。

況且還要改配置文件,WCF的配置,如下下划線部分。

<service name="MyNameSpace.Service.ConcreteServiceProxy" behaviorConfiguration="WFServiceBehavior">

V4.2.IOC版

到使用中間層為止,我已經是能夠接受的了。但老大覺得還可以再精簡,確實是經驗豐富,又被指點了,然后提點我使用IOC,目標是去除中間層——ConcreteServiceProxy。

 

思路:

1) 先使用動態代理創建一個被代理過的(Proxied)ConcreteService對象;

2) 將此對象放入IOC中(如Autofac,Unity等);

3) 如果需要使用ConcreteService類型的實例,從IOC中獲取即可。

 

注:(去除了中間層——ConcreteServiceProxy;同時ConcreteService不用加try-catch;也不用改配置文件了)

 

代碼4:去除了中間層——ConcreteServiceProxy;同時ConcreteService不用加try-catch;也不用改配置文件了

public class DependencyRegistrar : IDependencyRegistrar
{
    public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder)
    {
        var proxiedService = ServiceDynamicProxyInterceptor.CreateServiceProxy<ConcreteService>();
        builder.Register(c => proxiedServicec).As<ConcreteService>().InstancePerRequest();
    }

    public int Order
    {
        get { return 0; }
    }
}

 

一次多好的體驗啊!!!

用DP(Dynamic Proxy)完成了攔截;用IOC完成了DI。

 

寫到這里,問題來了,WCF能夠通過配置文件配置的服務名稱,即MyNameSpace.Service.ConcreteService,自動去IOC中找到被代理的對象嗎?Autofac.WCF能不能幫助它完成呢?

 (附)動態代理鏈接:

http://docs.castleproject.org/Tools.DynamicProxy.ashx

http://www.cnblogs.com/daxnet/archive/2011/09/07/2169520.html

http://www.cnblogs.com/RicCC/archive/2010/03/15/castle-dynamic-proxy.html

總結:就先寫到這里,體驗的感覺哪怕就是一點點,也很爽!歡迎拍磚。

Demo鏈接:http://files.cnblogs.com/pengzhen/TryCatchDemo.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM