真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

.NET中用戶端的防腐層作用及設(shè)計是怎樣的-創(chuàng)新互聯(lián)

本篇文章為大家展示了.NET中用戶端的防腐層作用及設(shè)計是怎樣的,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

創(chuàng)新互聯(lián)建站是一家專業(yè)從事成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司。作為專業(yè)的建站公司,創(chuàng)新互聯(lián)建站依托的技術(shù)實力、以及多年的網(wǎng)站運營經(jīng)驗,為您提供專業(yè)的成都網(wǎng)站建設(shè)、成都全網(wǎng)營銷及網(wǎng)站設(shè)計開發(fā)服務(wù)!

1.背景介紹

隨著現(xiàn)在的企業(yè)應(yīng)用架構(gòu)都在向著SOA方向轉(zhuǎn)變,目的就是將一個龐大的業(yè)務(wù)系統(tǒng)按照業(yè)務(wù)進行劃分,不管從公司的管理上、產(chǎn)品的開發(fā)上,這一系列流程來看,都是正確的。SOA確實帶來了解決現(xiàn)在大型企業(yè)級應(yīng)用系統(tǒng)快速膨脹的解決辦法。

但是本文要說的是,我們都將目光轉(zhuǎn)向到了后端,也就是服務(wù)端,而將精力和時間都重點投在了后端服務(wù)的架構(gòu)設(shè)計上,漸漸的忽視了顯示端的架構(gòu)設(shè)計。然而顯示端的邏輯也越來越復雜,顯示端輕薄的架構(gòu)其實已經(jīng)浮現(xiàn)出難以應(yīng)付后端服務(wù)接口快速膨脹的危險,服務(wù)接口都是按照指數(shù)級增加,基本上每一個新的業(yè)務(wù)需求都是提供新的接口,這沒有問題。按照服務(wù)的設(shè)計原則,服務(wù)接口就應(yīng)該有著明確的作用,而不是按照代碼的思維來考慮接口的設(shè)計。

但是由此帶來的問題就是組合這些接口的顯示端的結(jié)構(gòu)是否和這種變化是一致的,是否做好了這種變化帶來顯示端邏輯復雜的準備。

根據(jù)我自己的親身體會,我發(fā)現(xiàn)顯示端的架構(gòu)設(shè)計不被重視,這里的重視不是老板是否重視,而是我們開發(fā)人員沒有重視,當然這里排除時間問題。我觀察過很多用戶接口項目架構(gòu),結(jié)構(gòu)及其簡單,沒有封裝、沒有重用,看不到任何的設(shè)計原則。這樣就會導致這些代碼很難隨著業(yè)務(wù)的快速推動由服務(wù)接口帶來的沖擊,這里還有一個大的問題就是,作為程序員的我們是否有快速重構(gòu)的意識,我很喜歡這條程序員職業(yè)素質(zhì)。它可以讓我們敏捷的、快速的跟上由業(yè)務(wù)的發(fā)展帶來的項目結(jié)構(gòu)的變化。

迭代重構(gòu)對項目有著微妙的作用,重構(gòu)不能夠過早也不能夠過遲,要剛好在需要的時候重構(gòu)。對于重構(gòu)我的經(jīng)驗就是,當你面對新功能寫起來比較蹩腳的時候時,這是一個重構(gòu)信號,此時應(yīng)該是最優(yōu)的重構(gòu)時間。重構(gòu)不是專門的去準備時間,而是穿插在你寫代碼的過程中,它是你編碼的一部分。所以我覺得TDD被人接受的理由也在于此。

2.SOA架構(gòu)下的顯示端架構(gòu)腐化

顯示端的架構(gòu)腐化我個人覺得有兩個問題導致,第一個,原本顯示端的結(jié)構(gòu)在傳統(tǒng)系統(tǒng)架構(gòu)中可以工作的很好,但是現(xiàn)在的整體架構(gòu)變了,所以需要及時作出調(diào)整。第二,顯示端的架構(gòu)未能及時的重構(gòu),未能將顯示端結(jié)構(gòu)進行進一步分離,將顯示邏輯獨立可測試。

這樣隨著SOA接口的不斷增加,顯示端直接將調(diào)用服務(wù)的方法嵌入到顯示邏輯中,如,ASP.NET Mvc、ASP.NET Webapi的控制器中,包括兩個層面之間的DTO轉(zhuǎn)換。

按照DDD的上下文設(shè)計方法,在用戶顯示端也是可以有選擇的創(chuàng)建面向顯示的領(lǐng)域模型,此模型主要處理領(lǐng)域在即將到達服務(wù)端之后的前期處理。畢竟一個領(lǐng)域?qū)嶓w有著多個方面的職責,如果能在顯示端建立起輕量級的領(lǐng)域模型,對顯示邏輯的重構(gòu)將大有好處,當然前提是你有著復雜的領(lǐng)域邏輯。(我之前的上一家公司(美國知名的電子商務(wù)平臺),他們的顯示端有著復雜的領(lǐng)域邏輯,就光一個顯示端就復雜的讓人吃驚,如果能在此基礎(chǔ)上引入領(lǐng)域模型顯示端上下文,將對復雜的邏輯處理很有好好處,當然這只是我未經(jīng)驗證的猜測而已,僅供參考。)

對顯示端領(lǐng)域模型處理有興趣的可以參考本人寫的有關(guān)這方面的兩篇文章:

.NET應(yīng)用架構(gòu)設(shè)計—面向查詢的領(lǐng)域驅(qū)動設(shè)計實踐(調(diào)整傳統(tǒng)三層架構(gòu),外加維護型的業(yè)務(wù)開關(guān))

.NET應(yīng)用架構(gòu)設(shè)計—面向查詢服務(wù)的參數(shù)化查詢設(shè)計(分解業(yè)務(wù)點,單獨配置各自的數(shù)據(jù)查詢契約)

原本干凈的顯示邏輯多了很多無關(guān)的服務(wù)調(diào)用細節(jié),還有很多轉(zhuǎn)換邏輯,判斷邏輯,而這些東西原本不屬于這個地方,讓他們放在合適的地方對顯示邏輯的重構(gòu)、重用很有幫助。

如果不將其移出顯示邏輯中,那么隨著服務(wù)接口的不斷增加和擴展,將直接導致你修改顯示邏輯代碼,如果你的顯示邏輯代碼是MVC、Webapi共用的邏輯,那么情況就更加復雜了,最后顯示邏輯里面將被ViewModel與Service Dto之間的轉(zhuǎn)換占領(lǐng),你很難找到有價值的邏輯了。

3.有效使用防腐層來隔離碎片服務(wù)導致顯示端邏輯腐爛

解決這些問題的方法就是引入防腐層,盡管防腐層的初衷是為了解決系統(tǒng)集成時的領(lǐng)域模型之間的轉(zhuǎn)換,但是我覺得現(xiàn)在的系統(tǒng)架構(gòu)和集成有著很多相似之處,我們可以適當?shù)慕梃b這些好的設(shè)計方法來解決相似的問題。

引入防腐層之后,將原本不該出現(xiàn)在顯示邏輯中的代碼全部搬到防腐層中來,在防腐層中建立起OO機制,讓這些OO對象能夠和顯示邏輯一起搭配使用。

圖1:

.NET中用戶端的防腐層作用及設(shè)計是怎樣的

將用戶層分層三個子層,UiLayer,Show Logic Layer,Anticorrosive Layer,最后一個是服務(wù)的接口組,所有的服務(wù)接口調(diào)用均需要從防腐層走。

我們需要將Show Logic Layer中的服務(wù)調(diào)用,類型轉(zhuǎn)換代碼遷移到Anticorrsoive Layer中,在這里可以對象化轉(zhuǎn)換邏輯也可以不對象化,具體可以看下項目是否需要。如果業(yè)務(wù)確實比較復雜的時候,那么我們?yōu)榱朔庋b、重用就需要進行對象化。

4.剝離服務(wù)調(diào)用的技術(shù)組件讓其依賴接口

首先要做的就是將邏輯代碼中的服務(wù)對象重構(gòu)成面向接口的,然后讓其動態(tài)的依賴注入到邏輯類型中。在ASP.NETWEBAPI中,我們基本上將顯示邏輯都寫在這里面,我也將使用此方式來演示本章例子,但是如果你的MVC項目和WEBAPI項目共用顯示邏輯就需要將其提出來形成獨立的項目(Show Logic Layer)。

using OrderManager.Port.Models;
using System.Collections.Generic;
using System.Web.Http; 

namespace OrderManager.Port.Controllers
{
    public class OrderController : ApiController
    {
        [HttpGet]
        public OrderViewModel GetOrderById(long oId)
        {
            OrderService.Contract.OrderServiceClient client = new OrderService.Contract.OrderServiceClient();
            var order = client.GetOrderByOid(oId); 

            if (order == null) return null; 

            return AutoMapper.Mapper.DynamicMap(order);
        }
    }
}

這是一段很簡單的調(diào)用Order服務(wù)的代碼,首先需要實例化一個服務(wù)契約中包含的客戶端代理,然后通過代理調(diào)用遠程服務(wù)方法GetOrderByOid(long oId)。執(zhí)行一個簡單的判斷,最后輸出OrderViewModel。

如果所有的邏輯都這么簡單我想就不需要什么防腐層了,像這種類型的顯示代碼是極其簡單的,我這里的目的不是為了顯示多么的復雜的代碼如何寫,而是將服務(wù)調(diào)用調(diào)用的代碼重構(gòu)層接口,然后注入進OrderController實例中。目的就是為了能夠在后續(xù)的迭代重構(gòu)中對該控制器進行單元測試,這可能有點麻煩,但是為了長久的利益還是需要的。

using OrderManager.Port.Component;
using OrderManager.Port.Models;
using System.Collections.Generic;
using System.Web.Http; 

namespace OrderManager.Port.Controllers
{
    public class OrderController : ApiController
    {
        private readonly IOrderServiceClient orderServiceClient;
        public OrderController(IOrderServiceClient orderServiceClient)
        {
            this.orderServiceClient = orderServiceClient;
        } 

        [HttpGet]
        public OrderViewModel GetOrderById(long oId)
        {
            var order = orderServiceClient.GetOrderByOid(oId); 

            if (order == null) return null; 

            return AutoMapper.Mapper.DynamicMap(order);
        }
    }
}

為了能在運行時動態(tài)的注入到控制器中,你需要做一些基礎(chǔ)工作,擴展MVC控制器的初始化代碼。這樣我們就可以對OrderController進行完整的單元測試。

剛才說了,如果顯示邏輯都是這樣的及其簡單,那么一切都沒有問題了,真實的顯示邏輯非常的復雜而且多變,并不是所有的類型轉(zhuǎn)換都能使用Automapper這一類動態(tài)映射工具解決,有些類型之間的轉(zhuǎn)換還有邏輯在里面。GetOrderById(long oId)方法是為了演示此處的重構(gòu)服務(wù)調(diào)用組件用的。

大部分情況下我們是需要組合多個服務(wù)調(diào)用的,將其多個結(jié)果組合起來返回給前端的,這里的OrderViewModel對象里面的Items屬性類型OrderItem類型中包含了一個Product類型屬性,在正常情況下我們只需要獲取訂單的條目就行了,但是有些時候確實需要將條目中具體的產(chǎn)品信息也要返回給前臺進行部分信息的展現(xiàn)。

using System.Collections.Generic; 

namespace OrderManager.Port.Models
{
    public class OrderViewModel
    {
        public long OId { get; set; } 

        public string OName { get; set; } 

        public string Address { get; set; } 

        public List Items { get; set; }
    }
}

在OrderViewModel中的Items屬性是一個List集合,我們再看OrderItem屬性。

using System.Collections.Generic; 

namespace OrderManager.Port.Models
{
    public class OrderItem
    {
        public long OitemId { get; set; } 

        public long Pid { get; set; } 

        public float Price { get; set; } 

        public int Number { get; set; } 

        public Product Product { get; set; }
    }
}

它里面包含了一個Product實例,有些時候需要將該屬性賦上值。

namespace OrderManager.Port.Models
{
    public class Product
    {
        public long Pid { get; set; } 

        public string PName { get; set; } 

        public long PGroup { get; set; } 

        public string Production { get; set; }
    }
}

產(chǎn)品類型中的一些信息主要是用來作為訂單條目展現(xiàn)時能夠更加的人性化一點,你只給一個產(chǎn)品ID,不能夠讓用戶知道是哪個具體的商品。

我們接著看一個隨著業(yè)務(wù)變化帶來的代碼急速膨脹的例子,該例子中我們需要根據(jù)OrderItem中的Pid獲取Product完整信息。

using OrderManager.Port.Component;
using OrderManager.Port.Models;
using System.Collections.Generic;
using System.Web.Http;
using System.Linq; 

namespace OrderManager.Port.Controllers
{
    public class OrderController : ApiController
    {
        private readonly IOrderServiceClient orderServiceClient; 

        private readonly IProductServiceClient productServiceClient;
        public OrderController(IOrderServiceClient orderServiceClient, IProductServiceClient productServiceClient)
        {
            this.orderServiceClient = orderServiceClient;
            this.productServiceClient = productServiceClient;
        } 

        [HttpGet]
        public OrderViewModel GetOrderById(long oId)
        {
            var order = orderServiceClient.GetOrderByOid(oId); 

            if (order == null && order.Items != null && order.Items.Count > 0) return null; 

            var result = new OrderViewModel()
            {
                OId = order.OId,
                Address = order.Address,
                OName = order.OName,
                Items = new System.Collections.Generic.List()
            }; 

            if (order.Items.Count == 1)
            {
                var product = productServiceClient.GetProductByPid(order.Items[0].Pid);//調(diào)用單個獲取商品接口
                if (product != null)
                {
                    result.Items.Add(ConvertOrderItem(order.Items[0], product));
                }
            }
            else
            {
                List pids = (from item in order.Items select item.Pid).ToList(); 

                var products = productServiceClient.GetProductsByIds(pids);//調(diào)用批量獲取商品接口
                if (products != null)
                {
                    result.Items = ConvertOrderItems(products, order.Items);//批量轉(zhuǎn)換OrderItem類型
                } 

            } 

            return result;
        } 

        private static OrderItem ConvertOrderItem(OrderService.OrderItem orderItem, ProductService.Contract.Product product)
        {
            if (product == null) return null; 

            return new OrderItem()
            {
                Number = orderItem.Number,
                OitemId = orderItem.OitemId,
                Pid = orderItem.Pid,
                Price = orderItem.Price, 

                Product = new Product()
                {
                    Pid = product.Pid,
                    PName = product.PName,
                    PGroup = product.PGroup,
                    Production = product.Production
                }
            };
        } 

        private static List ConvertOrderItems(List products, List orderItems)
        {
            var result = new List(); 

            orderItems.ForEach(item =>
            {
                var orderItem = ConvertOrderItem(item, products.Where(p => p.Pid == item.Pid).FirstOrDefault());
                if (orderItem != null)
                    result.Add(orderItem);
            }); 

            return result;
        }
    }
}

我的第一感覺就是,顯示邏輯已經(jīng)基本上都是類型轉(zhuǎn)換代碼,而且這里我沒有添加任何一個有關(guān)顯示的邏輯,在這樣的情況下都讓代碼急速膨脹了,可想而知,如果再在這些代碼中加入顯示邏輯,我們基本上很難在后期維護這些顯示邏輯,而這些顯示邏輯才是這個類的真正職責。

由此帶來的問題就是重要的邏輯淹沒在這些轉(zhuǎn)換代碼中,所以我們急需一個能夠容納這些轉(zhuǎn)換代碼的位置,也就是防腐層,在防腐層中我們專門來處理這些轉(zhuǎn)換邏輯,當然我這里的例子是比較簡單的,只包含了查詢,真正的防腐層是很復雜的,它里面要處理的東西不亞于其他層面的邏輯處理。我們這里僅僅是在轉(zhuǎn)換一些DTO對象而不是復雜的DomainModel對象。

5.將服務(wù)的DTO與顯示端的ViewModel之間的轉(zhuǎn)換放入防腐層

我們需要一個防腐層來處理這些轉(zhuǎn)換代碼,包括對后端服務(wù)的調(diào)用邏輯,將這部分代碼移入防腐對象中之后會對我們后面重構(gòu)很有幫助。

namespace OrderManager.Anticorrsive
{
    using OrderManager.Port.Component;
    using OrderManager.Port.Models;
    using System.Collections.Generic;
    using System.Linq; 

    /// 
    /// OrderViewModel 防腐對象
    /// 
    public class OrderAnticorrsive : AnticorrsiveBase, IOrderAnticorrsive
    {
        private readonly IOrderServiceClient orderServiceClient; 

        private readonly IProductServiceClient productServiceClient; 

        public OrderAnticorrsive(IOrderServiceClient orderServiceClient, IProductServiceClient productServiceClient)
        {
            this.orderServiceClient = orderServiceClient;
            this.productServiceClient = productServiceClient;
        } 

        public OrderViewModel GetOrderViewModel(long oId)
        {
            var order = orderServiceClient.GetOrderByOid(oId); 

            if (order == null && order.Items != null && order.Items.Count > 0) return null; 

            var result = new OrderViewModel()
            {
                OId = order.OId,
                Address = order.Address,
                OName = order.OName,
                Items = new System.Collections.Generic.List()
            }; 

            if (order.Items.Count == 1)
            {
                var product = productServiceClient.GetProductByPid(order.Items[0].Pid);//調(diào)用單個獲取商品接口
                if (product != null)
                {
                    result.Items.Add(ConvertOrderItem(order.Items[0], product));
                }
            }
            else
            {
                List pids = (from item in order.Items select item.Pid).ToList(); 

                var products = productServiceClient.GetProductsByIds(pids);//調(diào)用批量獲取商品接口
                if (products != null)
                {
                    result.Items = ConvertOrderItems(products, order.Items);//批量轉(zhuǎn)換OrderItem類型
                } 

            } 

            return result;
        } 

        private static OrderItem ConvertOrderItem(OrderService.OrderItem orderItem, ProductService.Contract.Product product)
        {
            if (product == null) return null; 

            return new OrderItem()
            {
                Number = orderItem.Number,
                OitemId = orderItem.OitemId,
                Pid = orderItem.Pid,
                Price = orderItem.Price, 

                Product = new Product()
                {
                    Pid = product.Pid,
                    PName = product.PName,
                    PGroup = product.PGroup,
                    Production = product.Production
                }
            };
        } 

        private static List ConvertOrderItems(List products, List orderItems)
        {
            var result = new List(); 

            orderItems.ForEach(item =>
            {
                var orderItem = ConvertOrderItem(item, products.Where(p => p.Pid == item.Pid).FirstOrDefault());
                if (orderItem != null)
                    result.Add(orderItem);
            }); 

            return result;
        }
    }
}

如果你覺得有必要可以將IOrderServiceClient、IProductServiceClient 兩個接口放入AnticorrsiveBase基類中。

5.1.轉(zhuǎn)換邏輯過程化,直接寫在防腐層的方法中

對于防腐層的設(shè)計,其實如果你的轉(zhuǎn)換代碼不多,業(yè)務(wù)也比較簡單時,我建議直接寫成過程式的代碼比較簡單點。將一些可以重用的代碼直接使用靜態(tài)的擴展方法來使用也是比較簡單方便的,大問題就是不利于后期的持續(xù)重構(gòu),我們無法預知未來的業(yè)務(wù)變化,但是我們可以使用重構(gòu)來解決。

5.2.轉(zhuǎn)換邏輯對象化,建立起封裝、重用結(jié)構(gòu),防止進一步腐化

相對應(yīng)的,可以將轉(zhuǎn)換代碼進行對象化,形成防腐對象,每一個對象專門用來處理某一個業(yè)務(wù)點的數(shù)據(jù)獲取和轉(zhuǎn)換邏輯,如果你有數(shù)據(jù)發(fā)送邏輯那么將在防腐對象中大大獲益,對象化后就可以直接訂閱相關(guān)控制器的依賴注入事件,如果你是過程式的代碼想完成動態(tài)的轉(zhuǎn)換、發(fā)送、獲取會比較不方便。

6.防腐層的兩種依賴倒置設(shè)計方法

我們接著看一下如何讓防腐對象無干擾的進行自動化的服務(wù)調(diào)用和發(fā)送,我們希望防腐對象完全透明的在執(zhí)行著防腐的職責,并不希望它會給我們實現(xiàn)上帶來多大的開銷。

6.1.事件驅(qū)動(防腐層監(jiān)聽顯示邏輯事件)

我們可以使用事件來實現(xiàn)觀察者模式,讓防腐層對象監(jiān)聽某個事件,當事件觸發(fā)時,自動的處理某個動作,而不是要顯示的手動調(diào)用。

namespace OrderManager.Anticorrsive
{
    public interface IOrderAnticorrsive
    {
        void SetController(OrderController orderController); 

        OrderViewModel GetOrderViewModel(long oId);
    }
}

Order防腐對象接口,里面包含了一個void SetController(OrderController orderController); 重要方法,該方法是用來讓防腐對象自動注冊事件用的。

public class OrderController : ApiController
{
    private IOrderAnticorrsive orderAnticorrsive; 

    public OrderController(IOrderAnticorrsive orderAnticorrsive)
    {
        this.orderAnticorrsive = orderAnticorrsive; 

        this.orderAnticorrsive.SetController(this);//設(shè)置控制器到防腐對象中
    } 

    public event EventHandler SubmitOrderEvent; 

    [HttpGet]
    public void SubmitOrder(OrderViewModel order)
    {
        this.SubmitOrderEvent(this, order);
    }
}

在控制器中,每當我們發(fā)生某個業(yè)務(wù)動作時只管觸發(fā)事件即可,當然主要是以發(fā)送數(shù)據(jù)為主,查詢可以直接調(diào)用對象的方法。因為防腐對象起到一個與后臺服務(wù)集成的橋梁,當提交訂單時可能需要同時調(diào)用很多個后臺服務(wù)方法,用事件處理會比較方便。

/// 
    /// OrderViewModel 防腐對象
    /// 
    public class OrderAnticorrsive : AnticorrsiveBase, IOrderAnticorrsive
    {
        public void SetController(OrderController orderController)
        {
            orderController.SubmitOrderEvent += orderController_SubmitOrderEvent;
        } 

        private void orderController_SubmitOrderEvent(object sender, OrderViewModel e)
        {
            //提交訂單的邏輯
        }
    }
}

6.2.依賴注入接口

依賴注入接口是完全為了將控制器與防腐對象之間隔離用的,上述代碼中我是將接口定義在了防腐對象層中,那么也就是說控制器對象所在的項目需要引用防腐層,在處理事件和方法同時使用時會顯得有點不倫不類的,既有接口又有方法,其實這就是一種平衡吧,越純粹的東西越要付出一些代價。

如果我們定義純粹的依賴注入接口讓防腐對象去實現(xiàn),那么在觸發(fā)事件時就需要專門的方法來執(zhí)行事件的觸發(fā),因為不在本類中的事件是沒辦法觸發(fā)的。

上述內(nèi)容就是.NET中用戶端的防腐層作用及設(shè)計是怎樣的,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。


文章名稱:.NET中用戶端的防腐層作用及設(shè)計是怎樣的-創(chuàng)新互聯(lián)
瀏覽路徑:http://weahome.cn/article/djgeos.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部