设计模式实战之——创建型模式(Builder、Factory method、Abstract factory)

要深入学习面向对象,了解设计模式并在编程实践中不断实践是一条必经之路。之前读过《大话设计模式》,通过易懂的例子来说明每个设计模式的用法和效果,但是读完之后感觉很难在实际中运用,就是缺乏实践练习,没有深入思考,而且23个设计模式一下子接收起来很容易就弄混淆,而且对于适用场合也特别难以分清。最近一段时间开始重新学习,找来了《设计模式——可复用面向对象软件基础》这本被称为设计模式圣经的书,书的编排很好,每个模式按照意图、别名、动机、适用性、结构等12个方面依次阐述,非常适合不断思考和学习。

由于需要,我使用C#实现了一个抓取信息的类库,中间特别联系使用了Builder、Abstract factory模式,同时区分了Abstract factory和Factory method模式的区别。

首先,对于HttpWebRequest对象(产品)的构建使用了Builder模式,此模式适用性如下:

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
  •  当构造过程必须允许被构造的对象有不同的表示时
由于,发起http请求的时候需要根据不同的网站设置不同的请求头、cookie等信息,这就是不同的httpwebrequest对象,而且构建这种不同的对象需要关注其构建的细节,而不是整个产品。这也就是Builder和Abstract factoryFactory method的区别,后两者都是关注产品整体或一系列产品,并不关注产品构造的细节。此处构造的只有一个ConcreteBuilder,即HttpRequestBuilder,因此可以将Builder的抽象类略去,而在Director类中关联HttpRequestBuilder类的对象。
HttpRequestBuilder类如下:
public class HttpRequestBuilder
    {
        private HttpWebRequest _httpRequest;

        public bool SetCookie(Dictionary<string,string> key_val)
        {
            if (key_val == null)
            {
                return false;
            }
            CookieContainer cc = new CookieContainer(100,20,4000);
            foreach(KeyValuePair<string,string> kvp in key_val)
            {
                Cookie cookie = new Cookie(kvp.Key,kvp.Value);
                cc.Add(cookie);
            }
            _httpRequest.CookieContainer = cc;
            return true;
        }

        public bool SetConnection(string connection)
        {
            if (string.IsNullOrEmpty(connection))
            {
                return false;
            }
            _httpRequest.Connection = connection;
            return true;
        }

        public bool SetUA(string ua)
        {
            if (string.IsNullOrEmpty(ua))
            {
                return false;
            }
            _httpRequest.UserAgent = ua;
            return true;
        }

        public bool SetReferer(string referer)
        {
            if (string.IsNullOrEmpty(referer))
            {
                return false;
            }
            _httpRequest.Referer = referer;
            return true;
        }

        public bool SetMethod(string method = "GET")
        {
            if (string.IsNullOrEmpty(method))
            {
                return false;
            }
            _httpRequest.Method = method;
            return true;
        }

        public bool SetTimeOut(int time)
        {
            if (time <= 0)
            {
                return false;
            }
            _httpRequest.Timeout = time;
            return true;
        }

        public void SendRequest(string url)
        {
            _httpRequest = (HttpWebRequest)WebRequest.Create(url);
        }

        public HttpWebRequest GetObject()
        {
            return _httpRequest;
        }
    }

相应的Director类如下,此处的Director封装了三种构建方式,这是可以对产品的具体构建步骤进行精确控制的结果,方便以后继续构造不同的对象。
class HttpRequestDirector
    {
        private HttpRequestBuilder hrb;

        /// <summary>
        /// Normal construct HttpWebRequest object, method by GET
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public HttpRequestBuilder NormalConstruct(string url)
        {
            hrb = new HttpRequestBuilder();
            hrb.SetReferer("http://www.google.com");
            hrb.SetTimeOut(1000 * 60 * 5);
            hrb.SendRequest(url);
            return hrb;
        }

        /// <summary>
        /// Strict(with cookies) construct HttpWebRequest object, method by GET
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public HttpRequestBuilder StrictConstruct(string url,Dictionary<string,string> cookies)
        {
            hrb = new HttpRequestBuilder();
            hrb.SetConnection("keep-alive");
            hrb.SetCache(DateTime.Now, true);
            hrb.SetCookie(cookies);
            hrb.SetMethod("GET");
            hrb.SetReferer("http://www.google.com");
            hrb.SetUA("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31");
            hrb.SetTimeOut(1000 * 60 * 5);
            hrb.SendRequest(url);
            return hrb;
        }

        /// <summary>
        /// Post data
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public HttpRequestBuilder PostConstruct(string url)
        {
            hrb = new HttpRequestBuilder();
            hrb.SetConnection("keep-alive");
            hrb.SetMethod("POST");
            hrb.SetReferer("http://www.google.com");
            hrb.SetUA("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31");
            hrb.SetTimeOut(1000 * 60 * 10);
            hrb.SendRequest(url);
            return hrb;
        }
    }

然后,在Cralwer抽象类中定义了一组方法,需要有HttpCrawler、FtpCrawler等不同的Crawler对象,这是分别属于不同的等级结构的对象,同时存在三个产品族,分别是NormalCrawler、StrictCrawler和PostCrawler,因此就可以使用抽象工厂类构建这些对象。
抽象工厂适用性如下:
  • 一个系统要独立于它的产品的创建、组合和表示时
  •  一个系统要由多个产品系列中的一个来配置时、
  •  当你要强调一系列相关的产品对象的设计以便进行联合使用时
  •  当你提供一个产品类库,而只想显示它们的接口而不是实现时
而对于如果只希望使用HttpCrawler的情况,就只有一个产品等级结构,因此使用工厂方法构建就可以。在此处就深刻体会到了抽象工厂和工厂方法的区别,而且也特别区别了这两个模式关注的是产品整体的构建,而不是注重具体细节。工厂方法的适用性如下:
当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候
上述的一些应用和理解也当然受个人水平限制是有限的,当然还需要深入学习和思考。


你可能感兴趣的:(设计模式实战之——创建型模式(Builder、Factory method、Abstract factory))