C# 抽象工厂模式(花园规划工厂)

发布于:2025-08-29 ⋅ 阅读:(18) ⋅ 点赞:(0)

花园规划工厂

我们考虑一个实际的例子,在应用程序里要用到抽象工厂。假设读者编写一个程序来完成花园
的规划,这可能是季生棺物型花园、蔬菜型花园或多年生植物型花园。不管读者规划哪一种类型的
花园,都会遇到同样的问题:
1.边缘种什么植物?
2.中央种植什么植物?
3·阴凉部分种植什么?
(可能还会有其他许多种植问题,我们在这里不予考虑。)
这里想设计一个C#基类Garden,用类中的方法回答这些问题。

public class Garden
{
    protected Plant center,shade,border;
    protected bool showCenter,showShade,showBorder;

    public void setCenter(){showCenter=true;}
    public void setBorder(){showBorder=true;}
    public void setShade(){showShadetrue;}

    public void draw(Graphics g)
    {
        if(showCenter) center.draw(g,100,1000);
        if(showShade) shade.draw(g,10,50);
        if(showBorder)border.draw(g,50,100);
    }
}

Plant对象设置植物名字,在draw方法被调用时会绘制自己。

public class Plant
{
    private string name;
    private Brush br;
    private Font font;

    public Plant(string pname)
    {
        name=pname;
        font=new Font("Arial",12);
        br=new SolidBrush(Color.Black);
    }

    public void draw(Graphics g,int x,int y)
    {
        g.DrawString(name,font,br,x,y);
    }
}

用设计模式术语来讲,Garden接口就是抽象工厂。它定义了具体类中的方法,并返回一系列
相关类中的某个类。这里将中央植物、边缘植物和喜阴植物作为返回的三个相关类。抽象工厂也能
返回更具体的花园信息,例如土壤的PH值或灌溉需求等。

在实际系统中,规划每一种类型的花园都要查阅一个详尽的植物信息库,而在这个简单的例子
里,每类植物只给出一种。例如,对于蔬菜型花园,只给出下列几种植物:

public class VeggieGarden:VeggieGarden{
    public VeggieGarden()
    {
        shade=new Plant("Broccoli");
        border=new Plant("Peas");
        center=new Plant("Corn");
    }
}

我们用类似的方法创建Garden类的两个子类:PerennialGarden和AnnualGarden。因为每个具
体类都实现了父类中的方法,所以都可以看做一个具体工厂。现在我们有了一系列的Garden对象,
每一个对象都能创建一类Plant对象。

我们很容易构建出抽象工厂的驱动程序,它根据用户选择的单选按钮返回一个Garden对象。
每次选择一种新的花园类型时,都要清除屏幕,将复选框置为未选中状态。然后,选择一个复
选框,画出相应的植物类型。
记住,C#不需要你在程序里直接对屏幕绘图,血是在pt事件发生时由系统刷新屏幕,你只
要告诉paint例程绘制什么对象。

由于每种花园(及植物)都要知道如何绘制自己,所以应该有一个draw方法,在屏幕上画出
相应的植物名。由于我们用复选框来说明要画的植物类型,所以设置了一个布尔型变量,用它指出
要画的每种植物类型。

Garden对象包含了三个设置方法,用于指出绘制的每一种类型的植物。

public void setCenter(){showCenter=true;}
public void setBorder(){showBorder=true;}
public void setShade(){showShade=true;}

图片框

我们在图片框(PictureBox)里用圆表示阴影区域,在图片框中还要给出植物的名字。从
PictureBox类派生出一个新类GardenPic,并将画圆和花园植物名等信息传递给它,这是完成上述任
务的最好方法。因而,不仅需要在GardenMaker窗口类中添加一个Paint方法,还要在该窗口所包
含的PictureBox类中也添加该方法。它盖了基类Control中的OnPaint事件。

public class GdPic:System.Windows.Forms.PictureBox
{
    private Container components=null;
    private Brush br;
    private Garden gden;

    private void init()
    {
        br=new SolidBrush(Color.LightGray);
    }

    public GdPic()
    {
        InitializeComponent();
        init();
    }

    public void setGarden(Garden garden)
    {
        gden=garden;
    }

    protected override void OnPaint(PaintEventArgs pe)
    {
        Graphics g=pe.Graphics;
        g.FillEllipse(br,5,5,100,100);
        if(gden!=null)
            gden.draw(g);
    }
}

单击三个单选按钮中的某一个,就会创建一个该类型的花园,并把它传给图片框,还要清除所
有的复选框。

处理单选按钮和按钮事件

单击三个单选按钮中的某一个,就会创建一个该类型的花园,并把它传给图片框,还要清除所
有的复选框。

private void opAnnual_CheckedChanged(object sender,EventArgs e)
{
    setGarden(new AnnualGarden());
}

private void opVegetable_CheckedChanged(object sender,EventArgs e)
{
    setGarden(new VeggleGarden());
}

private void opPerennial_CheckedChanged(object sender,EventArgs e)
{
    setGarden(new PerennialGarden());
}

private void setGarden(Garden gd)
{
    garden=gd;
    gdPic1.setGarden(gd);
    gdPic1.Refresh();
    ckCenter.Checked=false;
    cdBorder.Checked=false;
    ckShade.Checked=false;
}

单击一个复选框去显示相应的植物名,要调用对应类型的花园方法去设置要显示的植物名,然
后调用图片框的Refresh方法使之重画。

private void ckCenter_CheckedChanged(object sender,SystemArgs e)
{
    garden.setCenter();
    gdPicl.Refresh();
}

private void chkBorder_CheckedChanged(object sender,System.EventArgs e)
{
    garden.setBorder();
    gdPicl.Refresh();
}

private void ckShade_CheckedChanged(object sender,System.EventArgs e)
{
    garden.setShade();
    gdPicl.Refresh();
}

添加更多的类

抽象工厂的一个强大功能是很容易添加新的子类。例如,如果需要一个GrassGarden类或
WildFlowerGarden类,则通过子类化Garden就可以创建这些类。在现有代码中,惟一需要读者真
正改动的是添加某种方法来选择这些新类型的花园。

抽象工厂的效果

抽象工厂的一个主要目的是它能隔离要生成的具体类。这些类的实际类名隐藏在工厂里,在客
户端根本不需要知道。

由于类的隔离,可以自由改动或交换这些生成类系列。此外,由于只生成一类具体的类,系统
会避免读者误用不同生成系列中的类。但是,添加新的类系列要费一些心思,因为读者要定义一些
新的、无二义性的条件使工厂能够返回新的类系列。

尽管抽象工厂生成的所有类都具有相同的基类,但还是无法避免某些子类具有额外的、与其他
类不同的方法。例如,BonsalGarden类有一个或WateringFrequency方法,这是其他类所没
有的。在其他子类中也存在同样的问题:你不知道能否调用一个类方法,除非知道该子类是否允许
使用那些方法。这个问题有两种解决方案:或者在基类中定义所有的方法,即使这些方法没有实际
的功能;或者是再派生出一个新的基类接口,它包含读者需要的所有方法和所有花园类型的子类。


网站公告

今日签到

点亮在社区的每一天
去签到