8

www.dofactory.com 上,我找到了工厂模式的真实示例。但是代码会在 ReSharper 中生成关于构造函数中的虚拟成员调用的警告。

导致警告的代码如下:

abstract class Document
{
    private List<Page> _pages = new List<Page>();

    // Constructor calls abstract Factory method
    public Document()
    {
        this.CreatePages(); // <= this line is causing the warning
    }

    public List<Page> Pages
    {
        get { return _pages; }
    }

    // Factory Method
    public abstract void CreatePages();
}

class Resume : Document
{
    // Factory Method implementation
    public override void CreatePages()
    {
        Pages.Add(new SkillsPage());
        Pages.Add(new EducationPage());
        Pages.Add(new ExperiencePage());
    }
}

在消费代码中,您可以简单地使用:

Document document = new Resume();

我确实理解为什么在构造函数中调用虚拟成员是一个坏主意(如此所述)。

我的问题是如何重构它以便仍然使用工厂模式,但在构造函数中没有虚拟成员调用。

如果我只是CreatePages从构造函数中删除调用,消费者将不得不显式调用该CreatePages方法:

Document document = new Resume();
document.CreatePages();

我更喜欢Resume实际创建包含页面的简历所需的全部内容的情况。

4

4 回答 4

2

重构它的一种方法是预先传递页面,并将它们传递给受保护的构造函数:

public abstract class Document {
    protected Document(IEnumerable<Page> pages) {
        // If it's OK to add to _pages, do not use AsReadOnly
        _pages = pages.ToList().AsReadOnly();
    }
    // ...
}

public class Resume : Document {
    public Resume() : base(CreatePages()) {
    }
    private static IEnumerable<Page> CreatePages() {
        return new Page[] {
            new SkillsPage(),
            new EducationPage(),
            new ExperiencePage()
        };
    }
}

PS我不确定这与工厂方法有什么关系。您的帖子说明了模板方法模式

于 2012-06-29T21:21:21.687 回答
1

那这个呢?它使用延迟初始化,仅在需要时创建页面(而不是在构造函数中创建它们)

另外,请注意工厂方法的可见性已更改为protected将其隐藏以不被公众使用。

abstract class Document{
    protected List<Page> _pages = new List<Page>();

    // Constructor calls abstract Factory method
    public Document(){}

    public List<Page> Pages
    {
        get { CreatePages(); return _pages; }
    }

    // Factory Method
    protected abstract void CreatePages();
}

class Resume : Document{
    // Factory Method implementation
    protected override void CreatePages(){
       if(pages.Count == 0 ){
        _pages .Add(new SkillsPage());
        _pages .Add(new EducationPage());
        _pages .Add(new ExperiencePage());
       }
    }
}

编辑建议:我个人不喜欢这个全局_pages变量,因为如果在许多方法和线程之间共享它可能会给你带来麻烦。我宁愿选择 GoF 书中描述的工厂方法模式。这是我的建议:

abstract class Document{
    public IEnumerable<Page> Pages{
        get { return CreatePages();}
    }

    // Factory Method
    protected abstract IEnumerable<Page> CreatePages();
}

class Resume : Document{
    // Factory Method implementation
    protected override IEnumerable<Page> CreatePages(){
         List<Page> _pages = new List<Page>();
        _pages .Add(new SkillsPage());
        _pages .Add(new EducationPage());
        _pages .Add(new ExperiencePage());
        return _pages;
       }
    }
}
于 2012-06-29T21:24:28.493 回答
1

我的问题是如何重构它以便仍然使用工厂模式,但在构造函数中没有虚拟成员调用。

根据定义

在基于类的编程中,工厂方法模式是一种创建模式,它使用工厂方法来处理创建对象的问题,而无需指定将要创建的对象的确切类。

工厂方法不打算从构造函数中使用,因为将创建的对象的确切类在构造时是已知的。例如,

  1. 如果Document必须Pages在构建时全部创建,那么它可以在没有工厂方法的情况下完成,因为它完全知道要创建的所有页面

  2. 但是如果Document必须Pages在构建后稍后创建并且可能多次创建,那么工厂方法很有用

我更喜欢创建一个新的简历是实际创建包含页面的简历所需的全部情况。

因此,如果Pages要在构建时创建所有内容,则可以在不使用工厂方法模式的情况下重写这个具体示例:

public class Document
    private readonly PageList as IList(of IPage)

    public readonly property Pages as IEnumerable(of IPage)
        get
            return PageList
        end get
    end property

    public sub new()
        Me.PageList = new List(of IPage)
    end sub

    protected sub Add(paramarray Pages() as IPage)
        Me.Pages.AddRange(Pages)
    end sub
end public

public class Resume
    inherits Document

    public sub new()
        mybase.add(new SkillsPage, new EducationPage, new ExperiencePage)
    end sub
end class
  1. Add允许从构造函数中使用方法Resume,因为Document对象已完全构造并可以使用。

  2. 方法Add是受保护的,因此页面只能从Document类或派生类中添加。

于 2015-02-23T20:15:26.990 回答
0

你可以在财产本身做。

在这种情况下,Pages可以将属性标记为virtual基类。

的假设代码Resume可能如下所示:

    private List<Page> _pages  = null;
    public override List<Page> Pages
    {
          get 
          {  
                if(pages == null) 
                {
                  _pages = new List<Page>();
                  _pages .Add(new SkillsPage());
                  _pages .Add(new EducationPage());
                  _pages .Add(new ExperiencePage());
                }

                return _pages; 
           }

        }
    }

在这种情况下,页面将在首次访问Pages属性时创建,而不是ctor执行时创建。

于 2012-06-29T21:20:57.087 回答