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

これをリファクタリングする1つの方法は、ページを事前に渡し、保護されたコンストラクターに渡すことです。

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

私の質問は、コンストラクターで仮想メンバーを呼び出さずに、ファクトリパターンを引き続き使用するために、これをリファクタリングする方法です。

定義によると

クラスベースプログラミングでは、ファクトリメソッドパターンは、作成されるオブジェクトの正確なクラスを指定せずに、ファクトリメソッドを使用してオブジェクト作成の問題に対処する作成パターンです。

作成されるオブジェクトの正確なクラスは構築時にわかっているため、factoryメソッドをコンストラクターから使用することは意図されていません。例えば、

  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. オブジェクトは完全に構築されており、すぐに使用できるため、メソッドAddResumeコンストラクターから使用できます。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 に答える