• 设计模式 抽象工厂(Abstract Factory Pattern)转载


     抽象工厂模式,比工厂模式具有更高层次的抽象。当要返回一系列相关类中的某一个,而每个类都能根据需要返回不同的对象时,可以使用这种模式。换句话说,抽象工厂是一个工厂对象,他能返回一系列相关类中的一个类。可以用简单工厂决定返回那个类。通常认为,实验式的例子应该引用汽车制造产,我们希望丰田汽车完全使用丰田配件,而福特汽车完全使用福特配件。可以把每个汽车制造厂设想为抽象工厂,配件作为一组相关的类。

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

     1 using System;
    2 using System.Drawing ;
    3 namespace Gardener
    4 {
    5 /// <summary>
    6 /// Summary description for Garden.
    7 /// </summary>
    8 public class Garden {
    9 protected Plant center, shade, border;
    10 protected bool showCenter, showShade, showBorder;
    11 //select which ones to display
    12 public void setCenter() {showCenter = true;}
    13 public void setBorder() {showBorder =true;}
    14 public void setShade() {showShade =true;}
    15 //draw each plant
    16 public void draw(Graphics g) {
    17 if (showCenter) center.draw (g, 100, 100);
    18 if (showShade) shade.draw (g, 10, 50);
    19 if (showBorder) border.draw (g, 50, 150);
    20 }
    21 }
    22 }

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

     1 using System;
    2 using System.Drawing;
    3
    4 namespace Gardener
    5 {
    6 /// <summary>
    7 /// Summary description for Plant.
    8 /// </summary>
    9 public class Plant {
    10 private string name;
    11 private Brush br;
    12 private Font font;
    13
    14 public Plant(string pname) {
    15 name = pname; //save name
    16 font = new Font ("Arial", 12);
    17 br = new SolidBrush (Color.Black );
    18 }
    19 //-------------
    20 public void draw(Graphics g, int x, int y) {
    21 g.DrawString (name, font, br, x, y);
    22 }
    23 }
    24 }

      用设计模式术语来讲,Garden接口就是抽象工厂。它定义了具体类中的方法,并返回一系列相关类中的某个类。这里将中央植物,边缘植物
    喜阴植物作为三个相关类。抽象工厂也能返回更具体的花园信息,例如土壤的PH值或灌溉需等。
    在实际中,规划每一种类型的花园都要查阅一个详尽的植物信息库,而在这个简单的例子里,没类植物只给出一种。例如,对蔬菜花园,只给出下列几种植物

     1 using System;
    2
    3 namespace Gardener
    4 {
    5 /// <summary>
    6 /// Summary description for VeggieGarden.
    7 /// </summary>
    8 public class VeggieGarden : Garden {
    9 public VeggieGarden() {
    10 shade = new Plant("Broccoli");
    11 border = new Plant ("Peas");
    12 center = new Plant ("Corn");
    13 }
    14 }
    15 }

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

      我们很容易就构建出了工厂的驱动程序,他根据用户选的单选按钮返回一个Garden对象,用户界面如下。每一次选择一种新的花园类型时,都要屏幕,将复选框设置为未选中状态。然后选择一个复选框,画出相应的植物类型。

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

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

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

     1     public class GdPic : System.Windows.Forms.PictureBox 
    2 {
    3 /// <summary>
    4 /// Required designer variable.
    5 /// </summary>
    6 private System.ComponentModel.Container components = null;
    7 private Brush br;
    8 private Garden gden;
    9 private void init () {
    10 br = new SolidBrush (Color.LightGray );
    11 }
    12 public GdPic() {
    13 // This call is required by the Windows.Forms Form Designer.
    14 InitializeComponent();
    15 init();
    16 }
    17 public void setGarden(Garden garden) {
    18 gden = garden;
    19 }
    20 protected override void OnPaint ( PaintEventArgs pe ){
    21 Graphics g = pe.Graphics;
    22 g.FillEllipse (br, 5, 5, 100, 100);
    23 if(gden != null)
    24 gden.draw (g);
    25 }

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

     1         private void opAnnual_CheckedChanged(object sender, EventArgs e) {
    2 setGarden( new AnnualGarden ());
    3 }
    4 //-----
    5 private void opVegetable_CheckedChanged(object sender, EventArgs e) {
    6 setGarden( new VeggieGarden ());
    7 }
    8 //-----
    9 private void opPerennial_CheckedChanged(object sender, EventArgs e) {
    10 setGarden( new PerennialGarden ());
    11 }
    12 //-----
    13 private void setGarden(Garden gd) {
    14 garden = gd; //save current garden
    15 gdPic1.setGarden ( gd); //tell picture bos
    16 gdPic1.Refresh (); //repaint it
    17 ckCenter.Checked =false; //clear all
    18 ckBorder.Checked = false; //check
    19 ckShade.Checked = false; //boxes
    20 }
    单击一个复选框曲线是相应的植物名,要调用对应的花园方法去设置要显示的名称,然后调用图片框的Refresh方法重画。
     1         private void ckCenter_CheckedChanged(object sender, System.EventArgs e) {
    2 garden.setCenter ();
    3 gdPic1.Refresh ();
    4 }
    5 //-----
    6 private void ckBorder_CheckedChanged(object sender, System.EventArgs e) {
    7 garden.setBorder();
    8 gdPic1.Refresh ();
    9 }
    10 //-----
    11 private void ckShade_CheckedChanged(object sender, System.EventArgs e) {
    12 garden.setShade ();
    13 gdPic1.Refresh ();
    14 }
    抽象工厂效果
    抽象工厂的一个主要目的是要能隔离要生成的工具类。这些类的实际类名隐藏在工厂里,在客户端根本不知道。由于类的隔离,可以自由改动或交换这些
    生成类系列。此外由于只要生成一类具体的类,系统会避免读者误用不同生成系类中的类。但是添加新的类系列要费一些心思 ,因为读者要定义一些
    心的、无二义性的条件使工厂返回新的类系列。尽管抽象工厂生成的所有类都是有相同的基类,但还是无法避免某些子类具有额外的、与其他类不同的方法,
    这中问题有两种解决方案:或者在基类中定义所有的方法,或者在是再派生出一个新的基类接口,它包含读者需要的所有方法和所有花园类型的子类。
  • 相关阅读:
    uva 11080(二分图染色)
    poj 3255(次短路)
    uva 707(记忆化搜索)
    uva 436(floyd变形)
    uva 11748(求可达矩阵)
    uva 11573(bfs)
    Codeforces Round #226 (Div. 2) 解题报告
    uva 11354(最小瓶颈路--多组询问 MST+LCA倍增)
    uva 534(最小瓶颈路)
    uva 538(简单图论 入度出度)
  • 原文地址:https://www.cnblogs.com/jameslif/p/2494202.html
Copyright © 2020-2023  润新知