zoukankan      html  css  js  c++  java
  • 设计模式 抽象工厂(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 }
    抽象工厂效果
    抽象工厂的一个主要目的是要能隔离要生成的工具类。这些类的实际类名隐藏在工厂里,在客户端根本不知道。由于类的隔离,可以自由改动或交换这些
    生成类系列。此外由于只要生成一类具体的类,系统会避免读者误用不同生成系类中的类。但是添加新的类系列要费一些心思 ,因为读者要定义一些
    心的、无二义性的条件使工厂返回新的类系列。尽管抽象工厂生成的所有类都是有相同的基类,但还是无法避免某些子类具有额外的、与其他类不同的方法,
    这中问题有两种解决方案:或者在基类中定义所有的方法,或者在是再派生出一个新的基类接口,它包含读者需要的所有方法和所有花园类型的子类。
  • 相关阅读:
    c:forTokens标签循环输出
    jsp转long类型为date,并且格式化
    spring中@Param和mybatis中@Param使用区别(暂时还没接触)
    734. Sentence Similarity 有字典数组的相似句子
    246. Strobogrammatic Number 上下对称的数字
    720. Longest Word in Dictionary 能连续拼接出来的最长单词
    599. Minimum Index Sum of Two Lists两个餐厅列表的索引和最小
    594. Longest Harmonious Subsequence强制差距为1的最长连续
    645. Set Mismatch挑出不匹配的元素和应该真正存在的元素
    409. Longest Palindrome 最长对称串
  • 原文地址:https://www.cnblogs.com/jameslif/p/2494202.html
Copyright © 2011-2022 走看看