设计模式之-----建造者模式(创建型模式)

 一、概念:

①、什么是建造者模式?

建造者模式:将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。

②、建造者的特点:

建造者模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。

③、使用建造者的好处:

1> 使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要在定义一个具体的建造者就可以了。

2>  使用建造者模式,用户只需指定需要建造的类型就可以得到他们,而具体的建造过程和细节就不需要知道了。

④、什么时候需要使用建造者模式呢?

<1> 主要是用户创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。

<2> 建造者模式是当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式时适用的模式。

换言之:

1>需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
2>隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

二、类图

从上图中可以看到实现建造者模式至少需要4个角色:

1、director:指挥者。由指挥者根据需求决定调用哪个建造者生产哪种产品。

1、builder:建造者接口。定义了建造一个产品所必须的所有流程。所有的建造者都需要实现该类,以确保实现建造一个产品所需的流程。

2、concreteBuilder:具体建造者。每一个建造者,可以创建一种产品,产品细节在建造者中定义,因此多个产品对应多个建造者。

3、product:具体产品。

三、实例代码:

实例要求:用程序画小人,小人要有头、身体、两手、两脚

注意:此模式要在Windows 窗体应用(.NET Framework)里面创建,要有两个PictureBox控件,并且使用PictureBox控件的CreateGraphics()方法,用来显示小人。还要有一个Button控件,用来写主函数的代码,点击此控件执行代码,显示小人。

From窗体界面如图:

创建小人的类图:

简单说明:由图可知,抽象建造者聚合成指挥者,抽象建造者又有两个子类继承它,也就是:指挥者去指挥具体的某个建造者去建造小人,具体的建造方法也在建造者类中。

代码:

(一)、主函数:

1.实例化一支带有颜色参数的“笔”;

2.建造瘦小人:实例化瘦小人类的对象,且带有两个参数:

 参数1:PictureBox1控件的CreatGraphics方法,此方法表示PictureBox1控件的绘图图板。

 参数2:是“p”。也就是说用笔p画一个ptb瘦人,画在图板上

3.实例化指挥者:将建造者对象(瘦小人建造者)传给指挥者,也就是让指挥者去指挥具体的建造者,并且会调用建造者的方法去具体建造小人

4.使用指挥者对象,调用指挥者的CreatePerson()方法,其方法体是使用建造者对象pb去调用具体的建造者方法。

     private void button1_Click(object sender, EventArgs e)
        {
            Pen p = new Pen(Color.Red);//首先实例化一个系统封装好的Pen类的对象“p”,含有参数:红色的笔
            
            //建造瘦人小人,实例化瘦人的建造者,调用瘦人的建造方法
            PersonThinBuilder ptb = new PersonThinBuilder(pictureBox1.CreateGraphics(), p);//实例化一个瘦人类的对象“ptb”,含有两个参数:
              //参数1.PictureBox1控件的CreatGraphics方法,此方法表示PictureBox1控件的绘图图板。参数2是“p”。也就是说用笔p画一个ptb瘦人,画在图板上

            PersonDirector pdThin = new PersonDirector(ptb);//实例化一个指挥者类的对象,并将建造者对象传进来,也就是让指挥者去指挥具体的建造者,并且会调用建造者的方法去具体建造小人
            pdThin.CreatePerson();


            //下边是建造胖人小人,与建造瘦人类似,这时实例化的是胖人的建造者,也调用胖人的建造方法
            PersonFatBuilder pfb = new PersonFatBuilder(pictureBox2.CreateGraphics(), p);
            PersonDirector pdFat = new PersonDirector(pfb);
            pdFat.CreatePerson();
        }
    

 这里涉及到了PictureBox控件的CreateGraphics方法:

解释:调用某控件或窗体的CreateGraphics方法以获取对Graphics对象的引用,该对象表示该控件或窗体的绘图图面。如果想在已存在的窗体或控件上绘图,通常会使用此方法。

(二)、抽象建造者类:确定小人由哪些部分组成。并声明一个得到产品建造后的结果的方法GetResult(此例子中未体现)。

abstract class PersonBuilder//抽象的建造人的类
    {
        protected Graphics g;//Graphics是系统封装好的一个绘图类,这里声明一个绘图类的对象g
        protected Pen p;

        public PersonBuilder(Graphics g, Pen p)//构造方法,把实例化的Graphics类和Pen类的对象传进来
        {
            this.g = g;
            this.p = p;
        }

        //抽象建造者的建造方法,确定小人由哪几部分组成。
        public abstract void BuildHead();
        public abstract void BuildBody();
        public abstract void BuildArmLeft();
        public abstract void BuildArmRight();
        public abstract void BuildLegLeft();
        public abstract void BuildLegRight();
    }

这里声明了Graphics类的对象g,对Graphics类解释:可以理解为画笔,为我们提供了各种绘制图形。

个人理解:Graphics是一个画家,而创建Graphics的参数Handle或者Image等为画板,当Graphics进行绘制时需要的Brush,Pen等则为工具,Graphics的函数则为行为。可以点击 F1 查询MSDN具体了解以及其常使用的一些方法。

(MSDN解释Graphics类:Graphics类提供用于将对象绘制到显示设备的方法。 Graphics与特定的设备上下文相关联。)

这里的g为Graphics类的对象,表示GDI+绘图表面,是用于创建图形图像的对象。

(三)、具体的建造者----瘦人类和胖人类: 子类重写父类方法,建造哪种小人,就调用哪种小人的建造方法

  class PersonThinBuilder : PersonBuilder//瘦人类
    {
        public PersonThinBuilder(Graphics  g, Pen p) : base(g, p)//构造方法,直接继承父类
        { }

        //下边瘦人类都是重写父类的建造方法,建造瘦人时,会调用
        public override void BuildHead()
        {
            //绘制一个由边框定义的椭圆,该边框由矩形的左上角坐标、高度和宽度指定。
            g.DrawEllipse(p, 50, 20, 30, 30);
        }
        public override void BuildBody()
        {
            //绘制由坐标对、宽度和高度指定的矩形。
            g.DrawRectangle(p, 60, 50, 10, 50);
        }
        public override void BuildArmLeft()
        {
            // 绘制一条连接由坐标对指定的两个点的线条。
            g.DrawLine(p, 60, 50, 40, 100);
        }
        public override void BuildArmRight()
        {
            g.DrawLine(p, 70, 50, 90, 100);
        }
        public override void BuildLegLeft()
        {
            g.DrawLine(p, 60, 100, 45, 150);
        }
        public override void BuildLegRight()
        {
            g.DrawLine(p, 70, 100, 85, 150);
        }

    }


    class PersonFatBuilder : PersonBuilder//胖人类
    {
        public PersonFatBuilder(Graphics g, Pen p) : base(g, p)
        { }

        //下边是胖人类重写父类的建造方法,建造胖人时会调用
        public override void BuildHead()
        {
            g.DrawEllipse(p, 50, 20, 30, 30);
        }
        public override void BuildBody()
        {
            g.DrawEllipse(p, 45, 50, 40, 50);
        }
        public override void BuildArmLeft()
        {
            g.DrawLine(p, 50, 50, 30, 100);
        }
        public override void BuildArmRight()
        {
            g.DrawLine(p, 80, 50, 100, 100);
        }
        public override void BuildLegLeft()
        {
            g.DrawLine(p, 60, 100, 45, 150);
        }
        public override void BuildLegRight()
        {
            g.DrawLine(p, 70, 100, 85, 150);
        }
    }

 这里涉及到的方法有Graphics的DrawRectangle(); DrawEllipse(); DrawLine()。对于其中的5个参数,4个坐标,可以点击 F1 在MSDN里边具体查询研究,里边对此方法及其其方法重载解释非常清楚明了。

(四)、指挥者类-----建造哪种小人,就指挥哪种建造者

   class PersonDirector//指挥者类
    {
        private PersonBuilder pb;//声明一个建造者的对象pb
        public PersonDirector(PersonBuilder pb)//把建造者对象传给指挥者,让指挥者去指挥这个建造者建造小人
        {
            this.pb = pb;//构造方法传参
        }
        public void CreatePerson()//创建人的方法,具体的方法体是用建造者的对象,调用建造者的方法去建造小人
        {
            //用建造者的对象“pb”去调用建造者的建造方法。
//指挥者只是指挥建造者调用哪些方法,具体的实现时在建造者类中,并且建造哪种人就调用哪种人的建造方法
            pb.BuildHead();
            pb.BuildBody();
            pb.BuildArmLeft();
            pb.BuildArmRight();
            pb.BuildLegLeft();
            pb.BuildLegRight();
        }

 

四、建造者模式涉及到的原则:

①依赖倒转原则:抽象类不应该依赖细节,细节应该依赖于抽象。

②里氏代换原则:子类型必须能够替换掉它们的父类型。里氏代换的四层含义:

  •  子类必须完全实现父类的方法

        在类中调用其他类时务必使用父类或接口,如若不能,则说明类的设计已经违背LSP原则。

        如果子类不能完整地实现父类的方法,或者父类的方法在子类中发生了畸变,这建议断开父子继承关系,采用依赖、聚集、组合等方式代替继承。

  • 子类可以有自己的特性:即子类出现的地方父类未必可以出现。

  • 覆盖父类的方法时输入参数可以被放大:输入参数类型宽于父类的类型的覆盖范围,例如 hashmap -> map。

  • 覆盖父类的方法时输出参数可以被缩小

由于此模式中,子类可以里氏代换父类,子类也可以有自己的特性。所以如果需要某个子类实现特有的方法(如:让瘦人类小人带上帽子),则可以在子类中单独写上自己这个特有的方法,小编认为这也属于是满足了可扩展性。(如有不同想法,欢迎留言交流~~~~)

五、建造者模式的优缺点

优点:

1、易于解耦,将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品;
2、易于精确控制对象的创建,将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰;
3、易于拓展,增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则”。

缺点:

1、建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;
2、如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制;
3、如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

建造者模式先暂时理解到这里, 每一种模式都是为了解决某一类问题,模式的思想非常重要,好好学习呦!加油~~

若有不同见解,欢迎留言交流,每一次的思维碰撞,都是一次思想的延伸,一次豁然开朗的理解!

发布了63 篇原创文章 · 获赞 13 · 访问量 8531

猜你喜欢

转载自blog.csdn.net/Ginny97/article/details/103139311