中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

.NET框架設計(高級框架架構模式)—鈍化程序、邏輯凍結、凍結程序的延續、瞬間轉移

發布時間:2020-07-21 20:42:22 來源:網絡 閱讀:1504 作者:王清培 欄目:編程語言

閱讀目錄:

  • 1.開篇介紹

  • 2.程序書簽(代碼書簽機制)

    • 2.1ProgramBookmark 實現(使用委托來錨點代碼書簽)

    • 2.2ProgramBookmarkManager書簽管理器(對象化書簽集合的處理,IEnumerable<T>書簽管理)


  • 3.可恢復語句組件(將語句對象化)

    • 3.1可恢復語句組件管理器(將可恢復語句視為普通的對象成員,IEnumerable<T>可恢復語句組件)

    • 3.2可恢復語句組件運行時(Program CLR(簡介))

    • 3.3可恢復語句邏輯配置(規則的配置(簡介))

    • 3.4可恢復語句邏輯傳輸(將邏輯語句對象遠程傳輸(簡介))


  • 4.DomainModel規則引擎(規則持久化后管理配置(簡介))

1】開篇介紹

這一篇文章我早準備寫的,遲遲未寫的原因是它過于抽象不太容易表達,也很難掌握;之前對它的理解還處于比較簡單的功能性上,但是最近隨著對領域驅動設計及架構的研究,設計思想有了一個提升對它的理解也有了一個更清晰的輪廓,所以才敢下手去寫,這么好的一篇文章不能搞砸了;

“鈍化語句” 簡單描述:將基于棧的調用抽象成基于我們自己構建的虛擬運行時調用;

比如我們可以將普通的IF\ELSE調用進行對象化,然后就可以對他們進行面向對象的設計了;能做的事情就太多了,比如將所有的方法放入一個for循環語句組件當中去,它會自動的去循環執行,而不需要我們再去自己寫for語句;然后在此基礎上進行代碼書簽抽象對所有的代碼片段進行類似邏輯錨點的設定;

更嚇人的是可以瞬間將語句組件鈍化,其實也就是瞬間凍結然后持久化,在遙遠的地方再將它喚醒執行,很可能你的語句在你這臺電腦上執行了一半由于你臨時有事然后語句被鈍化,在另外一臺電腦上繼續你的工作,是不是很方便;當然它的使用方式多種多樣了;

我相信這篇文章絕對讓你對 .NET框架設計 感興趣,框架設計思想其實真的很美,讓人陶醉;

2】程序書簽(代碼書簽機制)

美好的一切都要有一個良性的開始,程序的鈍化少不了對程序的邏輯保存的功能;有一個連續的調用穿過N個方法,方法一調用方法二,方法二調用方法三,這樣的調用層次是根據業務的需求來定的,就好比一個復雜的業務邏輯這樣的處理下去合情合理;

那么什么是代碼書簽呢?其實我們仔細分析一下我們日常所寫的代碼基本上都是由方法組合而成,不管是實例類還是靜態類都是通過方法將彼此聯系起來,所有的業務邏輯都是包裝在方法的內部處理的,這里的代碼書簽就是方法的可持久化抽象;

試想一下,我們要想將程序的邏輯流程鈍化肯定是少不了對邏輯調用的保存;原本的程序邏輯是線程本地的執行路徑,屬于.NETCLR直接管理的,依賴于棧的執行,所以我們無法干預其生命周期過程,那么我們只有將它們對象化后才能由我們自己操控;

圖1:

.NET框架設計(高級框架架構模式)—鈍化程序、邏輯凍結、凍結程序的延續、瞬間轉移


上圖的意思是說在一個流程的開始到結束基本上三個重要環節,Begin\Processs…\End過程,在每個過程中需要不同的處理邏輯,在圖的偏上方,我們有三個ProcessName名稱的小方塊表示程序的調用順序,ProcessName1調用ProcessName2調用ProcessName3;

在ProcessName2的上面我們加了一個Bookmark的標記,表示我們這里所說的代碼書簽,通過代碼書簽我們就可以記錄下本次執行到哪里了,就好比我們在看書的時候都有一個買書時贈送的書簽卡,我們看到哪里就把這個書簽卡插在那里,當下次要看的時候直接找到這個書簽卡繼續看;

這里的代碼書簽跟這個是一樣的道理,理論就是這些我們下面通過示例代碼來親身體驗一下這種設計模式;

2.1】ProgramBookmark 實現(使用委托來錨定代碼書簽)

委托是天生的方法標簽,通過委托我們完全可以將一個實例的方法直接錨定下來;

【有關對委托的高級應用不太清楚的可以參見本人的這兩篇文章:

.NET框架設計(一:常被忽視的C#設計技巧).NET框架設計(二:常被忽視的框架設計技巧)

我們來構造代碼書簽對象:

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-10
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ProgramComponent
{
    using System;
    /// <summary>
    /// Program book mark.
    /// </summary>
    [Serializable]
    public class ProgramBookmark
    {
        /// <summary>
        /// Mark program book mark.
        /// </summary>
        /// <param name="name">Mark name.</param>
        /// <param name="continueAt">Program continue.</param>
        public ProgramBookmark(string name, ProgramBookmarkLocation continueAt)
        {
            this.markname = name;
            this.continueAt = continueAt;
        }
        private string markname;
        /// <summary>
        /// Book mark name.
        /// </summary>
        public string BookmarkName { get { return markname; } }
        private ProgramBookmarkLocation continueAt;
        /// <summary>
        /// Continue location.
        /// </summary>
        public ProgramBookmarkLocation ContinueAt { get { return continueAt; } }
        /// <summary>
        /// Program load data.
        /// </summary>
        public object Payload { get; set; }
    }
    /// <summary>
    /// Program book mark location.
    /// </summary>
    /// <param name="resumed">Resumed bookmark.</param>
    public delegate void ProgramBookmarkLocation(ProgramBookmark resumed);
}


這段代碼是對代碼書簽的抽象,構造函數傳入一個代碼書簽的名稱、書簽所表示的物理代碼錨點,Payload是表示每次執行物理代碼時的輸入參數;

上面代碼看似簡單其實很不簡單,它的背后隱藏著一個很大的設計思想:

將一塊很大的邏輯代碼拆成很多零碎的方法片段,很多人可能會覺得設計本身不就這樣要求的嘛,那你可能真的沒有深入理解代碼碎片會后需要對所有的方法參數進行對象化,不管什么方法都會是同樣的參數,只有這樣才能讓書簽連續起作用;

下面我們來看一下代碼書簽有多巧妙,我們來構造一個簡單的示例代碼,當然你完全可以設計的很復雜很強大,這里畢竟是傳遞這種設計思想為主;


/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-10
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
    [Serializable]
    public class OrderCheckFlows
    {
        private IList<ProgramComponent.ProgramBookmark> flowsManager = new List<ProgramComponent.ProgramBookmark>();
        public OrderCheckFlows()
        {
            ProgramComponent.ProgramBookmark bookmarkCheckOrderPrices =
                new ProgramComponent.ProgramBookmark("checkPrices", new ProgramComponent.ProgramBookmarkLocation(CheckOrderPrices));
            flowsManager.Add(bookmarkCheckOrderPrices);
        }
        public void StartCheck()
        {
            do
            {
                flowsManager[0].ContinueAt(flowsManager[0]);
            }
            while (flowsManager.Count > 0);
        }
        #region business flows
        public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck)
        {
            Console.WriteLine("checkPrices...");
            ProgramComponent.ProgramBookmark bookmarkCheckOrderPrices =
                new ProgramComponent.ProgramBookmark("checkPrices", new ProgramComponent.ProgramBookmarkLocation(CheckOrderItems));
            bookmarkCheckOrderPrices.Payload = true;//method parameters.
            flowsManager.Add(bookmarkCheckOrderPrices);
            flowsManager.RemoveAt(0);
        }
        public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck)
        {
            if ((bool)nextCheck.Payload)
            {
                Console.WriteLine("checkItems...");
            }
            else
            {
                Console.WriteLine("end check items.");
            }
            flowsManager.RemoveAt(0);
        }
        #endregion
    }
}


這個類是一個簡單的模擬檢查訂單的一系列的業務流程;

圖2:

.NET框架設計(高級框架架構模式)—鈍化程序、邏輯凍結、凍結程序的延續、瞬間轉移

上圖能看見流程順利執行完畢了,那么我們來解釋一下重要的代碼片段;

圖3:

.NET框架設計(高級框架架構模式)—鈍化程序、邏輯凍結、凍結程序的延續、瞬間轉移

在第一個流程里面我們構造一個通往下一個流程的 ProgramComponent.ProgramBookmark 對象,如果這里出現關于流程無法繼續下去的條件就可以不創建往下執行的代碼書簽;在第二流程里面我們獲取第一個流程設置的參數,這里是一個Bool值,可以用來判斷上一個執行是否成功等信息;

2.2】ProgramBookmarkManager書簽管理器(書簽集合的處理,IEnumerable<T>書簽管理)

上一節我們完成了對代碼書簽的抽象實現,但是代碼還有很多值得抽象設計的地方,上面的代碼中最不太理解的地方就是對書簽集合的操作上,很不OO;

那么這一節我們將把它改進,形成OO方式的調用,先看一下哪里不太理解;

圖4:

.NET框架設計(高級框架架構模式)—鈍化程序、邏輯凍結、凍結程序的延續、瞬間轉移

第一個地方就是在聲明ProgramCompoent.ProgramBookmark集合上,這樣寫問題太大了,無法進行擴展改進;然后就是在構造函數中,我們使用了很長一段代碼來構造一個ProgramCompoent.ProgramBookmark對象,完全可以減少很多;還有就是在StartCheck方法的內部中進行循環調用書簽的代碼,也很有問題,完全可以封裝在內部實現,外部直接一個CurrentProgram屬性執行就行了;

那么對這些問題我們其實少一個ProgramCompoent.ProgramBookmark的管理器對象ProgramCompoent.ProgramBookmarkManager對象,它負責管理所有跟ProgramCompoent.ProgramBookmark對象相關的工作;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-10
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ProgramComponent
{
    using System.Collections.Generic;
    /// <summary>
    /// Program book mark Manager.<see cref="System.Collections.Dictionary{BookmarkName,ProgramBookmark}"/>
    /// </summary>
    public class ProgramBookmarkManager : Dictionary<string, ProgramBookmark>
    {
        /// <summary>
        /// Add programbookmark and instant next programbookmark.
        /// </summary>
        /// <param name="bookmark"><see cref="ProgramComponent.ProgramBookmark"/></param>
        public void Add(ProgramBookmark bookmark)
        {
            base.Add(bookmark.BookmarkName, bookmark);
        }
        /// <summary>
        /// Remove programbookmark.
        /// </summary>
        /// <param name="bookmark"><see cref="ProgramComponent.ProgramBookmark"/></param>
        public void Remove(ProgramBookmark bookmark)
        {
            base.Remove(bookmark.BookmarkName);
        }
        /// <summary>
        /// Resume  bookmark by bookmarkname.
        /// </summary>
        /// <param name="bookmarkName">bookmark name.</param>
        /// <param name="payload">Continue load.</param>
        public void Resume(string bookmarkName, object payload)
        {
            ProgramBookmark bookmark;
            this.TryGetValue(bookmarkName, out bookmark);
            if (bookmark != null)
            {
                bookmark.Payload = payload;
                bookmark.ContinueAt(bookmark);
            }
        }
    }
}

書簽管理器基本功能還算簡單,主要的方法Resume是用來恢復指定的書簽的;再來看一下訂單檢查流程調用;

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-10
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ConsoleApplication1
{
    using System;
    using ProgramComponent;
    [Serializable]
    public class OrderCheckFlows
    {
        private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager();
        public OrderCheckFlows()
        {
            BookmarkManager.Add(new ProgramBookmark("checkPrices", new ProgramBookmarkLocation(CheckOrderPrices)));
        }
        public void StartCheck()
        {
            BookmarkManager.Resume("checkPrices", null);
        }
        #region business flows
        public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck)
        {
            Console.WriteLine("checkPrices...");
            BookmarkManager.Remove(nextCheck);
            BookmarkManager.Add(new ProgramBookmark("checkItems", new ProgramBookmarkLocation(CheckOrderItems)));
            BookmarkManager.Resume("checkItems", true);
        }
        public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck)
        {
            if ((bool)nextCheck.Payload)
                Console.WriteLine("checkItems...");
            else
                Console.WriteLine("end check items.");
            BookmarkManager.Remove(nextCheck);
        }
        #endregion
    }
}


是不是比之前的代碼好多了,我感覺是好多了,當然還有很大的重構空間;

這里其實已經可以和鏈式編程的機制掛鉤了,我們可以通過給書簽管理器添加N個擴展方法來使書簽管理器具有跟鏈式的調用;

3】可恢復語句組件(將可恢復語句對象化)

要想把所有的調用都拆開來使用松散的方式組合,通過使用書簽機制基本上能將所有的方法進行松散組合;那么我們還需要將邏輯語法進行對象化才能做到無死角的松散;

什么叫語句組件,就是將一些原本無法獨立的一些邏輯判斷、循環之類的語句對象化,形成更具有對象的組件;

試想一下,如果我們將所有的這些邏輯語法對象化后我們的代碼中還有精密耦合的代碼嗎?就算有也應該會很少,是不是很神奇;

其實對 企業應用架構 中的 規約模式 有所了解的人應該會比較熟悉這一節的內容,跟規約模式很像,但不是一個東西,側重點不同;語句組件全面的概念是將所有的調用都對象化,包括一些輸出、輸入、網絡調用等等,這樣才是全部的語句組件定義,還記得我們上面的訂單檢查對象嘛,那個也是語句組件之一;

我們來構造可恢復語句組件對象;

ProgramComponent.LanguageComponent.LanguageComponent類代碼:

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-10
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ProgramComponent.LanguageComponent
{
    using System;
    [Serializable]
    public abstract class LanguageComponent
    {
        public abstract void Run(ProgramBookmarkManager mgr);
    }
}

ProgramComponent.LanguageComponent.LanguageComponentBlock類代碼:

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-10
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ProgramComponent.LanguageComponent
{
    using System.Collections.Generic;
    public class LanguageComponentBlock : LanguageComponent
    {
        List<LanguageComponent> statements = new List<LanguageComponent>();
        public List<LanguageComponent> Statements
        {
            get { return statements; }
        }
        public void AddLangugateComponent(LanguageComponent lc)
        {
            statements.Add(lc);
        }
        public override void Run(ProgramBookmarkManager mgr)
        {
                                                                    
        }
    }
}

ProgramComponent.LanguageComponent.IfElseLanguageComponent類代碼:

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-10
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ProgramComponent.LanguageComponent
{
    using System;
    using System.Linq;
    using System.Linq.Expressions;
    public class IfElseLanguageComponent : LanguageComponentBlock
    {
        public Func<bool> Exp { get; set; }
        public override void Run(ProgramBookmarkManager mgr)
        {
            if (Exp())
                base.Statements[0].Run(mgr);
            else
                base.Statements[1].Run(mgr);
        }
    }
}

檢查流程代碼,OrderCheckFlows\OrderSubmitFlows類代碼:

/*==============================================================================
 * Author:深度訓練
 * Create time: 2013-08-10
 * Blog Address:http://www.cnblogs.com/wangiqngpei557/
 * Author Description:特定領域軟件工程實踐;
 *==============================================================================*/
namespace ConsoleApplication1
{
    using System;
    using ProgramComponent;
    using ProgramComponent.LanguageComponent;
    [Serializable]
    public class OrderCheckFlows : LanguageComponent
    {
        private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager();
        public OrderCheckFlows(ProgramBookmarkManager bookmarkManager)
        {
            this.BookmarkManager = bookmarkManager;
            BookmarkManager.Add(new ProgramBookmark("checkPrices", new ProgramBookmarkLocation(CheckOrderPrices)));
        }
        public override void Run(ProgramBookmarkManager mgr)
        {
            this.BookmarkManager = mgr;
            StartCheck();
        }
        public void StartCheck()
        {
            BookmarkManager.Resume("checkPrices", null);
        }
        #region business flows
        public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck)
        {
            Console.WriteLine("checkPrices...");
            BookmarkManager.Remove(nextCheck);
            BookmarkManager.Add(new ProgramBookmark("checkItems", new ProgramBookmarkLocation(CheckOrderItems)));
            BookmarkManager.Resume("checkItems", true);
        }
        public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck)
        {
            if ((bool)nextCheck.Payload)
                Console.WriteLine("checkItems...");
            else
                Console.WriteLine("end check items.");
            BookmarkManager.Remove(nextCheck);
        }
        #endregion
    }
    [Serializable]
    public class OrderSubmitFlows : LanguageComponent
    {
        private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager();
        public OrderSubmitFlows(ProgramBookmarkManager bookmarkManager)
        {
            this.BookmarkManager = bookmarkManager;
            BookmarkManager.Add(new ProgramBookmark("CheckSubmitPrices", new ProgramBookmarkLocation(CheckSubmitPrices)));
        }
        public override void Run(ProgramBookmarkManager mgr)
        {
            this.BookmarkManager = mgr;
            StartCheck();
        }
        public void StartCheck()
        {
            BookmarkManager.Resume("CheckSubmitPrices", null);
        }
        #region business flows
        public void CheckSubmitPrices(ProgramComponent.ProgramBookmark nextCheck)
        {
            Console.WriteLine("CheckSubmitPrices...");
            BookmarkManager.Remove(nextCheck);
            BookmarkManager.Add(new ProgramBookmark("CheckSubmitItems", new ProgramBookmarkLocation(CheckSubmitItems)));
            BookmarkManager.Resume("CheckSubmitItems", true);
        }
        public void CheckSubmitItems(ProgramComponent.ProgramBookmark nextCheck)
        {
            if ((bool)nextCheck.Payload)
                Console.WriteLine("CheckSubmitItems...");
            else
                Console.WriteLine("end check CheckSubmitItems.");
            BookmarkManager.Remove(nextCheck);
        }
        #endregion
    }
}

調用代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
    using ProgramComponent;
    using ProgramComponent.LanguageComponent;
    class Program
    {
        static void Main(string[] args)
        {
            ProgramBookmarkManager bookmarkManager = new ProgramBookmarkManager();
            OrderCheckFlows orderCheckFlow = new OrderCheckFlows(bookmarkManager);
            OrderSubmitFlows submitCheckFlow = new OrderSubmitFlows(bookmarkManager);
            IfElseLanguageComponent languageComponent = new IfElseLanguageComponent();
            languageComponent.Exp = () => { return true; };
            languageComponent.AddLangugateComponent(orderCheckFlow);
            languageComponent.AddLangugateComponent(submitCheckFlow);
            languageComponent.Run(bookmarkManager);
            Console.ReadLine();
        }
    }
}

一切都已經被對象化,我們來看一下邏輯;

圖5:

.NET框架設計(高級框架架構模式)—鈍化程序、邏輯凍結、凍結程序的延續、瞬間轉移

這里的返回值決定了后面要執行的語句組件的路徑,如果是true,則應該檢查OrderCheckFlows流程;

圖6:

.NET框架設計(高級框架架構模式)—鈍化程序、邏輯凍結、凍結程序的延續、瞬間轉移

如果是false,則應該檢查OrderSubmitFlows流程;

圖7:

.NET框架設計(高級框架架構模式)—鈍化程序、邏輯凍結、凍結程序的延續、瞬間轉移

可恢復語句對象模型基本構造完成,當然復雜的問題還需要仔細的去分析設計,這里只是一個簡單的示例;

3.1】可恢復語句組件管理器(將可恢復語句視為普通的對象成員,IEnumerable<T>語句組件)

跟代碼書簽管理器一個道理,這里我們也可以實現一個LanguageComponentManager來對LanguageComponent管理,當然也要看需要不需要;可恢復語句管理器其實有很多文章可以做,因為它是所有語句組件的中心,這對于后面的持久化有很大的用處;

//由于內容比較多且相當抽象,下一篇文章介紹;

3.2】可恢復語句組件運行時(Program CLR)

所有的語句代碼都已經被對象化,但是在運行時需要一個中心來管理這些被對象化的語句組件,因為我們要脫離對棧的依賴;一組語句組件是單個示例流程的一部分,但是我們可能會存在很多一起并行運行的流程,所以這是必須要提供的運行時;

//由于內容比較多且相當抽象,下一篇文章介紹;

3.3】可恢復語句邏輯配置(規則的配置)

領域驅動設計在使用規約模式的時候會存在動態配置的需求,可以參見這里的語句組件模型,讓規約最大化的提供配置;

//由于內容比較多且相當抽象,下一篇文章介紹;

3.4】可恢復語句邏輯傳輸(將可恢復語句對象遠程傳輸)

//由于內容比較多且相當抽象,下一篇文章介紹;

4】DomainModel規則引擎(規則持久化后管理配置)

//由于內容比較多且相當抽象,下一篇文章介紹;



示例Demo地址:http://files.cnblogs.com/wangiqngpei557/ConsoleApplication3.zip

作者:王清培

出處:http://wangqingpei557.blog.51cto.com/

本文版權歸作者和51CTO共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

龙游县| 吉隆县| 三门县| 六安市| 呼玛县| 宁国市| 延川县| 扬州市| 竹北市| 瑞安市| 新津县| 双城市| 枝江市| 奎屯市| 大兴区| 哈尔滨市| 永城市| 瑞丽市| 和林格尔县| 梅河口市| 万年县| 德安县| 木兰县| 新乡县| 民县| 舒兰市| 历史| 师宗县| 新安县| 锡林郭勒盟| 牡丹江市| 外汇| 上杭县| 镇原县| 嘉鱼县| 浑源县| 北碚区| 手机| 河曲县| 宁武县| 高碑店市|