• 设计模式应用案例(上)


    设计模式应用案例(上)

    前文一共介绍了四人帮(Gang of Four)总结的的11个设计模式,对初学者而言,光看文字描述和UML类图略显抽象。本着Learning in Doing的原则,本文将举一些实际的业务需求场景,以C#代码为例,讲述在编程的过程中如何应用设计模式,实现模块间低耦合,高内聚,编写出优雅的代码。需说明的是,接下来的例子相对简单,省略了业务逻辑代码,目的是为了让大家专注于设计模式的应用,忽略业务逻辑本身的复杂性,毕竟本文的目的是加深对设计模式本身的理解。

    一.Façade模式

    业务场景:圣象饮料公司需汇总每个季度矿泉水的销量,并和竞争对手的销量进行比较,并且以季度报表的形式汇报管理层。当前的系统情况是:统计内部商品的销量由类SelfProduct、竞争对手商品的销量由类CompetitorProject负责,该需求用Facade模式实现如下:

    复制代码
    namespace PartternCase
    {
        public class SelfProduct
        {//Class A in UML
            public decimal StatSales(DateTime start, DateTime end, string kind);
        }
    
        public class CompetitorProduct
        {//Class B in UML
            public decimal StatSales(DateTime start, DateTime end, string kind);
        }
    
        public class ReportFacade
        {//Facade in UML
            private SelfProduct Self { get; set; }
            private CompetitorProduct Competitor { get; set; }
    
            public ReportFacade(SelfProduct self, CompetitorProduct competitor)
            {
                Self = self;
                Competitor = competitor;
            }
    
            public Tuple<decimal, decimal> StatSelling(DateTime start, DateTime end)
            {//Interface for client call
                return Tuple.Create(Self.StatSales(start, end, "MineralWater"), Competitor.StatSales(start, end, "MineralWater"));
            }
        }
    }
    复制代码

    二.Adapter模式

    业务场景:需要提供一个快速创建Word文档的API,微软提供的Office对象模型太复杂,创建Word时需要传入太多的缺省参数。下面是用对象Adapter模式实现的代码(C#不支持多继承,在此不介绍类Adapter模式,况且也不推荐使用):

    复制代码
    namespace PartternCase
    {
        public interface IWordAdapter
        {//Target in UML
            //Interface for client call
            void CreateWord(string filePath);
        }
    
        public class WordAdapter : IWordAdapter
        {//Adapter in UML
            public void CreateWord(string filePath)
            {
                Object Nothing = System.Reflection.Missing.Value;
                //MS default word creator is Adaptee
                Application wordApp = new Microsoft.Office.Interop.Word.ApplicationClass();
                Document wordDoc = wordApp.Documents.Add(ref Nothing, ref Nothing, ref Nothing, ref Nothing);
                wordDoc.SaveAs(ref filePath, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, 
                    ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing, ref Nothing);
                wordDoc.Close(ref Nothing, ref Nothing, ref Nothing);
                wordApp.Quit(ref Nothing, ref Nothing, ref Nothing);
            }
        }
    }
    复制代码

    三.Strategy模式

    业务场景:假设用户熟悉每种排序算法的应用场景,需要在界面自己选择排序算法(众所周知,排序算法有很多种,各自适合不同的场景),将一组数字排序后,打印在窗口。采用策略模式实现的代码如下:

    复制代码
    namespace PartternCase
    {
        public class SequencePrinter
        {//Context in UML
            private Sorter Sorter { get; set; }
    
            public SequencePrinter(Sorter sorter)
            {//Interface for client call
                Sorter = sorter;
            }
    
            public void Perform(IList<int> sequences)
            {//Assign relative sorter by criteria at runtime context
                sequences = Sorter.Rank(sequences);
                foreach (var sequence in sequences)
                {
                    Console.WriteLine("Number is " + sequence);
                }
            }
        }
    
        public abstract class Sorter
        {//Strategy in UML
            public abstract IList<int> Rank(IList<int> source);
        }
    
        public class BubbleSorter : Sorter
        {//ConcreteStrategyA in UML 
            public override IList<int> Rank(IList<int> source);
        }
    
        public class QuickSorter : Sorter
        {//ConcreteStrategyB in UML
            public override IList<int> Rank(IList<int> source);
        }
    }
    复制代码

    四.Bridge模式

    业务场景:将上述Strategy模式的业务场景做一个扩展,正好就是采用Bridge模式的最佳场景,即:用户可以在界面选择外排序算法和打印终端,将一组数据排序后,打印到相应的终端上。采用Bridge模式的实现代码如下:

    复制代码
    namespace PartternCase
    {
        public abstract class AbstractSequencePrinter
        {//Abstraction in UML
            protected Sorter Sorter { get; set; }
    
            protected AbstractSequencePrinter(Sorter sorter)
            {//Assign relative sorter by criteria at runtime context
                Sorter = sorter;
            }
    
            public abstract void Perform(IList<int> sequences);
        }
    
        public class ConsoleSequencePrinter : AbstractSequencePrinter
        {//RefinedAbstraction in UML
            public ConsoleSequencePrinter(Sorter sorter)
                : base(sorter)
            {
            }
    
            public override void Perform(IList<int> sequences)
            {
                sequences = Sorter.Rank(sequences);
                foreach (var sequence in sequences)
                {
                    Console.WriteLine("Number is " + sequence);
                }
            }
        }
    
        public abstract class Sorter
        {//Implementor in UML
            public abstract IList<int> Rank(IList<int> source);
        }
    
        public class BubbleSorter : Sorter
        {//ConcreteImplementorA in UML
            public override IList<int> Rank(IList<int> source);
        }
    
        public class QuickSorter : Sorter
        {//ConcreteImplementorB in UML
            public override IList<int> Rank(IList<int> source);
        }
    }
    复制代码

    五.Abstract Factory模式

    业务场景:圣天基金公司旗下有两种私募基金产品:主基金(Master Fund)和子基金(Feeder Fund)。两种基金的提款(Capital Call)规则和分配(Distribution)规则都存在明显差异,要求主基金的提款规则只能和主基金的分配规则搭配使用,不允许混淆。采用Abstract Factory模式的实现代码如下:

    复制代码
    namespace PartternCase
    {
        public abstract class AbstractFund
        {//Abstract Factory
            //Interface for client call
            public abstract CapitalCallRule CreateCallRule();
            public abstract DistributionRule CreateDistribRule();
        }
    
        public class MasterFund : AbstractFund
        {//ConcreteFactory1 in UML
            public override CapitalCallRule CreateCallRule()
            {
                return new MasterCallRule();
            }
    
            public override DistributionRule CreateDistribRule()
            {
                return new MasterDistribRule();
            }
        }
    
        public class FeederFund : AbstractFund
        {//ConcreteFactory2 in UML
            public override CapitalCallRule CreateCallRule()
            {
                return new FeederCallRule();
            }
    
            public override DistributionRule CreateDistribRule()
            {
                return new FeederDistribRule();
            }
        }
    
        public abstract class CapitalCallRule
        {//AbstractProductA in UML
        }
    
        public class MasterCallRule : CapitalCallRule
        {//ConcreteProductA1 in UML
        }
    
        public class FeederCallRule : CapitalCallRule
        {//ConcreteProductA2 in UML
        }
    
        public abstract class DistributionRule
        {//AbstractProductB in UML
        }
    
        public class MasterDistribRule : DistributionRule
        {//ConcreteProductB1 in UML
        }
    
        public class FeederDistribRule : DistributionRule
        {//ConcreteProductB2 in UML
        }
    }
    复制代码
    【版权所有,转载请注明文章出处:Maxwell Zhou
     

    Radix Sort

    为了完成二维数据快速分类,最先使用的是hash分类。

    前几天我突然想,既然基数排序的时间复杂度也不高,而且可能比hash分类更稳定,所以不妨试一下。

    在实现上我依次实现:

    1、一维数组基数排序

    基本解决主要问题,涵盖排序,包含改进的存储分配策略。

    如果用链表来实现,大量的函数调用将耗费太多时间。

    2、二维数组基数排序

    主要是实现和原有程序的集成。

    一、数据结构

    下面是存储节点的主数据结构。

    复制代码
    typedef struct tagPageList{
        int * PagePtr;
        struct tagPageList * next;
    }PageList;
    
    typedef struct tagBucket{
        int * currentPagePtr;
        int offset;
        PageList pl;
        PageList * currentPageListItem;
    }Bucket;
    复制代码

    链表内是存储的一个4KB页面的指针。

    每4KB页面可以存储最多1024个记录序号,如果是一维数组排序,那就直接存储数组元素了。

    二、算法

    基数排序可以分为MSD或者LSD。这里用的是LSD。

    伪代码如下:

    复制代码
    for i=0 to sizeof(sorted-element-type){
        for each sorted-num{
            cell = sorted-num
            bucketIdx = (cell>>8*i)&0xff
            bucket[bucketIdx] = cell
        }
        combine linked list nodes to overwrite original array
    }
    复制代码

    C代码实现:

    复制代码
    int main(){
        HANDLE heap = NULL;
        Bucket bucket[BUCKETSLOTCOUNT];
        PageList * pageListPool;
        int plpAvailable = 0;
        int * pages = NULL;
        int * pagesAvailable = NULL;
        int * objIdx;
        unsigned short * s;
    
        time_t timeBegin;
        time_t timeEnd;
    
        heap = HeapCreate(HEAP_NO_SERIALIZE|HEAP_GENERATE_EXCEPTIONS, 1024*1024, 0);
        if (heap != NULL){
            pages = (int * )HeapAlloc(heap, 0, (TFSI/PAGEGRANULAR + BUCKETSLOTCOUNT + 8) * 4096);
            pageListPool = (PageList *)HeapAlloc(heap, 0, (TFSI/PAGEGRANULAR + 8) * sizeof(PageList));
            s = (unsigned short *)HeapAlloc(heap, 0, TFSI*sizeof(unsigned short));
            objIdx = (int *)HeapAlloc(heap, 0, TFSI * sizeof(int));
        }
        MakeSure(pages != NULL && pageListPool != NULL && objIdx != NULL);
    
        for(int i=0; i<TFSI; i++) objIdx[i]=i;
        timeBegin = clock();
        for (int i=0; i<TFSI; i++) s[i] = rand();
        timeEnd = clock();
        printf("
    %f(s) consumed in generating numbers", (double)(timeEnd-timeBegin)/CLOCKS_PER_SEC);
        
        timeBegin = clock();
    
        for (int t=0; t<sizeof(short); t++){
            FillMemory(pages, (TFSI/PAGEGRANULAR + BUCKETSLOTCOUNT + 8) * 4096, 0xff);
            SecureZeroMemory(pageListPool, (TFSI/PAGEGRANULAR + 8) * sizeof(PageList));
            pagesAvailable = pages;
            plpAvailable = 0;
    
            for(int i=0; i<256; i++){
                bucket[i].currentPagePtr = pagesAvailable;
                bucket[i].offset = 0;
                bucket[i].pl.PagePtr = pagesAvailable;
                bucket[i].pl.next = NULL;
                pagesAvailable += PAGEGRANULAR;
                bucket[i].currentPageListItem = &(bucket[i].pl);
            }
    
            int bucketIdx;
            for (int i=0; i<TFSI; i++){
                bucketIdx = (s[objIdx[i]]>>t*8)&0xff;
                MakeSure(bucketIdx < 256);
                //save(bucketIdx, objIdx[i]);
                bucket[bucketIdx].currentPagePtr[ bucket[bucketIdx].offset ] = objIdx[i];
                bucket[bucketIdx].offset++;
                if (bucket[bucketIdx].offset == PAGEGRANULAR){
                    bucket[bucketIdx].currentPageListItem->next = &pageListPool[plpAvailable];
                    plpAvailable++;
                    MakeSure(plpAvailable < TFSI/PAGEGRANULAR + 8);
                    bucket[bucketIdx].currentPageListItem->next->PagePtr = pagesAvailable;
                    bucket[bucketIdx].currentPageListItem->next->next = NULL;
                    
                    bucket[bucketIdx].currentPagePtr = pagesAvailable;
                    bucket[bucketIdx].offset = 0;
                    pagesAvailable += PAGEGRANULAR;
                    MakeSure(pagesAvailable < pages+(TFSI/PAGEGRANULAR + BUCKETSLOTCOUNT + 8) * 1024);
                
                    bucket[bucketIdx].currentPageListItem = bucket[bucketIdx].currentPageListItem->next;
                }
            }
    
            //update objIdx index
            int start = 0;
            for (int i=0; i<256; i++){
                PageList * p;
                p = &(bucket[i].pl);
                while (p){
                    for (int t=0; t<PAGEGRANULAR; t++){
                        int idx = p->PagePtr[t];
                        if (idx != TERMINATOR){
                            objIdx[start] = idx;
                            start++;
                        }
                        if (idx == TERMINATOR) break;
                    }
                    p = p->next;
                }
            }
        }
    
        timeEnd = clock();
        printf("
    %f(s) consumed in generating results", (double)(timeEnd-timeBegin)/CLOCKS_PER_SEC);
    
        //for (int i=0; i<TFSI; i++) printf("%d
    ", s[objIdx[i]]);
    
        HeapFree(heap, 0, pages);
        HeapFree(heap, 0, pageListPool);
        HeapFree(heap, 0, s);
        HeapFree(heap, 0, objIdx);
        HeapDestroy(heap);
    
        return 0;
    }
    复制代码

    三、测试结果。

    i7 3632QM @2.2GHz ==>TB 3.2GHz/ 8G RAM/  win8 64bit/VS2012 win32 release 

    1024*1024*100,1亿个随机生成 short 型数据。 
    1.438000(s) consumed in generating random numbers 
    4.563000(s) consumed in radix sort  
    12.719000(s) consumed in qsort 
    7.641000(s) consumed in std::sort

     

    1024*1024*5   500万随机生成 short 型数据。 
    0.078000(s) consumed in generating random numbers 
    0.172000(s) consumed in radix sort  
    0.656000(s) consumed in qsort 
    0.390000(s) consumed in std::sort

     

    1024*500 
    0.000000(s) consumed in generating random numbers 
    0.015000(s) consumed in radix sort  
    0.063000(s) consumed in qsort 
    0.047000(s) consumed in std::sort

    四、讨论

    二维数据分类上,性能相当于hash分类 约 1/3 。

    比库例程稍快,慢的主要原因还是存储器,如果只是解决一维数组的话,调整下可以更快。

    但对于二维数组多个线程同时操作,排序是不可接受的。

     
     
    分类: C/C++
  • 相关阅读:
    css代码中position的定位,baidu+总结
    ibatis_HelloWorld
    v7系统,任务栏的开始图标和其他图标重合问题
    递归方法:输入一个多位整数,计算出从0到该数1出现的个数。
    解决JS:window.close()在Firefox下的不能关闭的问题
    Programming in the MidFuture(转)
    修改blog问题
    面向数据库的高级语言
    F#试用感受
    基于.net的数学编程语言
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3356291.html
Copyright © 2020-2023  润新知