Factory-Muster in C #: Wie kann sichergestellt werden, dass eine Objektinstanz nur von einer Factory-Klasse erstellt werden kann?

93

Vor kurzem habe ich darüber nachgedacht, einen Teil meines Codes zu sichern. Ich bin gespannt, wie man sicherstellen kann, dass ein Objekt niemals direkt erstellt werden kann, sondern nur über eine Methode einer Factory-Klasse. Angenommen, ich habe eine "Geschäftsobjekt" -Klasse und möchte sicherstellen, dass jede Instanz dieser Klasse einen gültigen internen Status hat. Um dies zu erreichen, muss ich einige Überprüfungen durchführen, bevor ich ein Objekt erstelle, wahrscheinlich in seinem Konstruktor. Dies ist alles in Ordnung, bis ich beschließe, dass diese Überprüfung Teil der Geschäftslogik sein soll. Wie kann ich also veranlassen, dass ein Geschäftsobjekt nur mit einer Methode in meiner Geschäftslogikklasse erstellt werden kann, jedoch niemals direkt? Der erste natürliche Wunsch, ein gutes altes "Freund" -Schlüsselwort von C ++ zu verwenden, wird mit C # nicht erfüllt. Wir brauchen also andere Optionen ...

Versuchen wir ein Beispiel:

public MyBusinessObjectClass
{
    public string MyProperty { get; private set; }

    public MyBusinessObjectClass (string myProperty)
    {
        MyProperty = myProperty;
    }
}

public MyBusinessLogicClass
{
    public MyBusinessObjectClass CreateBusinessObject (string myProperty)
    {
        // Perform some check on myProperty

        if (true /* check is okay */)
            return new MyBusinessObjectClass (myProperty);

        return null;
    }
}

Es ist alles in Ordnung, bis Sie sich daran erinnern, dass Sie die MyBusinessObjectClass-Instanz weiterhin direkt erstellen können, ohne die Eingabe zu überprüfen. Ich möchte diese technische Möglichkeit insgesamt ausschließen.

Was denkt die Community darüber?

Benutzer
quelle
Ist die MyBoClass-Methode falsch geschrieben?
Pradeeptp
2
Ich bin verwirrt, warum sollten Sie NICHT möchten, dass Ihre Geschäftsobjekte für den Schutz ihrer eigenen Invarianten verantwortlich sind? Der Sinn des Factory-Musters (so wie ich es verstehe) besteht darin, entweder A) die Erstellung einer komplexen Klasse mit vielen Abhängigkeiten zu behandeln oder B) die Factory entscheiden zu lassen, welcher Laufzeittyp zurückgegeben werden soll, wobei nur eine Schnittstelle / Zusammenfassung verfügbar gemacht wird Klasse an den Kunden. Das Einfügen Ihrer Geschäftsregeln in Ihre Geschäftsobjekte ist das Wesentliche von OOP.
Sara
@kai Richtig, das Geschäftsobjekt ist für den Schutz seiner Invariante verantwortlich, aber der Konstruktoraufrufer muss sicherstellen, dass Argumente gültig sind, bevor sie an den Konstruktor übergeben werden ! Der Zweck der CreateBusinessObjectMethode besteht darin, Argumente in einem einzelnen Methodenaufruf zu validieren UND (ein neues gültiges Objekt zurückzugeben ODER einen Fehler zurückzugeben), wenn ein Aufrufer nicht sofort weiß, ob Argumente für die Übergabe an den Konstruktor gültig sind.
Lightman
2
Ich bin nicht einverstanden. Der Konstruktor ist dafür verantwortlich, alle erforderlichen Überprüfungen für die angegebenen Parameter durchzuführen. Wenn ich einen Konstruktor aufrufe und keine Ausnahme ausgelöst wird, vertraue ich darauf, dass sich das erstellte Objekt in einem gültigen Zustand befindet. Es sollte physikalisch nicht möglich sein, eine Klasse mit den "falschen" Argumenten zu instanziieren. Das Erstellen einer DistanceInstanz mit einem negativen Argument sollte ArgumentOutOfRangeExceptionbeispielsweise eine auslösen. Sie würden nichts davon profitieren, wenn Sie eine Instanz erstellen DistanceFactory, die dieselbe Prüfung durchführt. Ich sehe immer noch nicht, was Sie hier gewinnen können.
Sara

Antworten:

55

Sieht so aus, als ob Sie vor dem Erstellen des Objekts nur eine Geschäftslogik ausführen möchten. Warum erstellen Sie also nicht einfach eine statische Methode in der "BusinessClass", die alle schmutzigen "myProperty" -Prüfungen ausführt, und machen den Konstruktor privat?

public BusinessClass
{
    public string MyProperty { get; private set; }

    private BusinessClass()
    {
    }

    private BusinessClass(string myProperty)
    {
        MyProperty = myProperty;
    }

    public static BusinessClass CreateObject(string myProperty)
    {
        // Perform some check on myProperty

        if (/* all ok */)
            return new BusinessClass(myProperty);

        return null;
    }
}

Es zu nennen wäre ziemlich einfach:

BusinessClass objBusiness = BusinessClass.CreateObject(someProperty);
Ricardo Nolde
quelle
6
Ah, Sie könnten auch eine Ausnahme auslösen, anstatt null zurückzugeben.
Ricardo Nolde
5
Es macht keinen Sinn, einen privaten Standardkonstruktor zu haben, wenn Sie einen benutzerdefinierten deklariert haben. Auch in diesem Fall kann durch die Verwendung eines statischen "Konstruktors" buchstäblich nichts gewonnen werden, anstatt nur die Validierung im realen Konstruktor durchzuführen (da dieser sowieso Teil der Klasse ist). Es ist sogar SCHLECHTER, da Sie jetzt einen Null-Rückgabewert erhalten können, der sich für NREs öffnet und "Was zum Teufel bedeutet diese Null?" Fragen.
Sara
Gibt es eine gute Möglichkeit, diese Architektur über eine Interface / abstrakte Basisklasse zu benötigen? dh eine Schnittstelle / abstrakte Basisklasse, die vorschreibt, dass Implementierer keinen ctor verfügbar machen sollen.
Arash Motamedi
1
@Arash Nein. Schnittstellen können keine Konstruktoren definieren, und eine abstrakte Klasse, die einen geschützten oder internen Konstruktor definiert, kann nicht verhindern, dass erbende Klassen ihn über einen eigenen öffentlichen Konstruktor verfügbar machen.
Ricardo Nolde
66

Sie können den Konstruktor privat und die Factory als verschachtelten Typ festlegen:

public class BusinessObject
{
    private BusinessObject(string property)
    {
    }

    public class Factory
    {
        public static BusinessObject CreateBusinessObject(string property)
        {
            return new BusinessObject(property);
        }
    }
}

Dies funktioniert, weil verschachtelte Typen Zugriff auf die privaten Mitglieder ihrer umschließenden Typen haben. Ich weiß, dass es ein bisschen restriktiv ist, aber hoffentlich hilft es ...

Jon Skeet
quelle
Ich habe darüber nachgedacht, aber es verschiebt diese Schecks effektiv in das Geschäftsobjekt selbst, was ich zu vermeiden versuche.
Benutzer
@ so-tester: Sie können die Prüfungen weiterhin im Werk und nicht im BusinessObject-Typ durchführen.
Jon Skeet
3
@ JonSkeet Ich weiß, dass diese Frage wirklich alt ist, aber ich bin gespannt, welchen Vorteil es hat, die CreateBusinessObjectMethode in eine verschachtelte FactoryKlasse zu integrieren, anstatt dass diese statische Methode eine Methode direkt der BusinessObjectKlasse ist. Können Sie sich an Ihre erinnern? Motivation dafür?
Kiley Naro
3
@KileyNaro: Nun, darum wurde die Frage gebeten :) Es bringt nicht unbedingt viele Vorteile, aber es beantwortet die Frage ... es kann Zeiten geben, in denen dies nützlich ist - das Builder-Muster fällt mir ein. (In diesem Fall wäre der Builder die verschachtelte Klasse, und es hätte eine Instanzmethode namens Build.)
Jon Skeet
53

Wenn Sie wirklich ausgefallen sein möchten, kehren Sie die Kontrolle um: Lassen Sie die Klasse die Factory zurückgeben und instrumentieren Sie die Factory mit einem Delegaten, der die Klasse erstellen kann.

public class BusinessObject
{
  public static BusinessObjectFactory GetFactory()
  {
    return new BusinessObjectFactory (p => new BusinessObject (p));
  }

  private BusinessObject(string property)
  {
  }
}

public class BusinessObjectFactory
{
  private Func<string, BusinessObject> _ctorCaller;

  public BusinessObjectFactory (Func<string, BusinessObject> ctorCaller)
  {
    _ctorCaller = ctorCaller;
  }

  public BusinessObject CreateBusinessObject(string myProperty)
  {
    if (...)
      return _ctorCaller (myProperty);
    else
      return null;
  }
}

:) :)

Fabian Schmied
quelle
Sehr schönes Stück Code. Die Objekterstellungslogik befindet sich in einer separaten Klasse, und Objekte können nur mit der Factory erstellt werden.
Nikolai Samteladze
Cool. Gibt es eine Möglichkeit, die Erstellung der BusinessObjectFactory auf das statische BusinessObject.GetFactory zu beschränken?
Felix Keil
1
Was ist der Vorteil dieser Inversion?
Yatskovsky
1
@yatskovsky Dies ist nur eine Möglichkeit, um sicherzustellen, dass das Objekt nur kontrolliert instanziiert werden kann, während die Erstellungslogik dennoch in eine dedizierte Klasse zerlegt werden kann.
Fabian Schmied
15

Sie können den Konstruktor in Ihrer MyBusinessObjectClass-Klasse intern erstellen und ihn und die Factory in eine eigene Assembly verschieben. Jetzt sollte nur die Factory in der Lage sein, eine Instanz der Klasse zu erstellen.

Matt Hamilton
quelle
7

Abgesehen von dem, was Jon vorgeschlagen hat, könnte die Factory-Methode (einschließlich der Prüfung) in erster Linie eine statische Methode von BusinessObject sein. Lassen Sie dann den Konstruktor privat, und alle anderen werden gezwungen, die statische Methode zu verwenden.

public class BusinessObject
{
  public static Create (string myProperty)
  {
    if (...)
      return new BusinessObject (myProperty);
    else
      return null;
  }
}

Die eigentliche Frage ist jedoch: Warum haben Sie diese Anforderung? Ist es akzeptabel, die Fabrik oder die Fabrikmethode in die Klasse zu verschieben?

Fabian Schmied
quelle
Dies ist kaum eine Voraussetzung. Ich möchte nur eine saubere Trennung von Geschäftsobjekt und Logik. Ebenso wie es unangemessen ist, diese Überprüfungen im Code-Behind der Seiten zu haben, halte ich es für unangemessen, diese Überprüfungen in Objekten selbst durchzuführen. Nun, vielleicht grundlegende Validierung, aber nicht wirklich Geschäftsregeln.
Benutzer
Wenn Sie die Logik und die Struktur einer Klasse nur zur Überprüfung der Bequemlichkeit trennen möchten, können Sie immer eine
Teilklasse verwenden
7

Nach so vielen Jahren wurde dies gefragt, und alle Antworten, die ich sehe, sagen Ihnen leider, wie Sie Ihren Code machen sollten, anstatt eine klare Antwort zu geben. Die eigentliche Antwort, nach der Sie gesucht haben, besteht darin, dass Ihre Klassen einen privaten Konstruktor, aber einen öffentlichen Instanziator haben. Dies bedeutet, dass Sie nur neue Instanzen aus anderen vorhandenen Instanzen erstellen können ... die nur im Werk verfügbar sind:

Die Schnittstelle für Ihre Klassen:

public interface FactoryObject
{
    FactoryObject Instantiate();
}

Deine Klasse:

public class YourClass : FactoryObject
{
    static YourClass()
    {
        Factory.RegisterType(new YourClass());
    }

    private YourClass() {}

    FactoryObject FactoryObject.Instantiate()
    {
        return new YourClass();
    }
}

Und schließlich die Fabrik:

public static class Factory
{
    private static List<FactoryObject> knownObjects = new List<FactoryObject>();

    public static void RegisterType(FactoryObject obj)
    {
        knownObjects.Add(obj);
    }

    public static T Instantiate<T>() where T : FactoryObject
    {
        var knownObject = knownObjects.Where(x => x.GetType() == typeof(T));
        return (T)knownObject.Instantiate();
    }
}

Anschließend können Sie diesen Code problemlos ändern, wenn Sie zusätzliche Parameter für die Instanziierung oder die Vorverarbeitung der von Ihnen erstellten Instanzen benötigen. Mit diesem Code können Sie die Instanziierung durch die Factory erzwingen, da der Klassenkonstruktor privat ist.

Alberto Alonso
quelle
4

Eine weitere (leichte) Option besteht darin, eine statische Factory-Methode in der BusinessObject-Klasse zu erstellen und den Konstruktor privat zu halten.

public class BusinessObject
{
    public static BusinessObject NewBusinessObject(string property)
    {
        return new BusinessObject();
    }

    private BusinessObject()
    {
    }
}
Dan C.
quelle
2

Es sieht also so aus, als ob das, was ich will, nicht "rein" gemacht werden kann. Es ist immer eine Art "Rückruf" an die Logikklasse.

Vielleicht könnte ich es auf einfache Weise tun, einfach eine Konstruktormethode in der Objektklasse erstellen und zuerst die Logikklasse aufrufen, um die Eingabe zu überprüfen?

public MyBusinessObjectClass
{
    public string MyProperty { get; private set; }

    private MyBusinessObjectClass (string myProperty)
    {
        MyProperty  = myProperty;
    }

    pubilc static MyBusinessObjectClass CreateInstance (string myProperty)
    {
        if (MyBusinessLogicClass.ValidateBusinessObject (myProperty)) return new MyBusinessObjectClass (myProperty);

        return null;
    }
}

public MyBusinessLogicClass
{
    public static bool ValidateBusinessObject (string myProperty)
    {
        // Perform some check on myProperty

        return CheckResult;
    }
}

Auf diese Weise kann das Geschäftsobjekt nicht direkt erstellt werden, und die öffentliche Überprüfungsmethode in der Geschäftslogik schadet ebenfalls nicht.

Benutzer
quelle
2

Bei einer guten Trennung zwischen Schnittstellen und Implementierungen ermöglicht das
Muster des geschützten Konstruktor-Public-Initialisierers eine sehr saubere Lösung.

Bei einem Geschäftsobjekt:

public interface IBusinessObject { }

class BusinessObject : IBusinessObject
{
    public static IBusinessObject New() 
    {
        return new BusinessObject();
    }

    protected BusinessObject() 
    { ... }
}

und eine Geschäftsfabrik:

public interface IBusinessFactory { }

class BusinessFactory : IBusinessFactory
{
    public static IBusinessFactory New() 
    {
        return new BusinessFactory();
    }

    protected BusinessFactory() 
    { ... }
}

Die folgende Änderung am BusinessObject.New()Initialisierer ergibt die Lösung:

class BusinessObject : IBusinessObject
{
    public static IBusinessObject New(BusinessFactory factory) 
    { ... }

    ...
}

Hier wird ein Verweis auf eine konkrete Geschäftsfabrik benötigt, um den BusinessObject.New()Initialisierer aufzurufen . Aber der einzige, der die erforderliche Referenz hat, ist die Business Factory selbst.

Wir haben bekommen, was wir wollten: Der einzige, der schaffen kann, BusinessObjectist BusinessFactory.

Reuven Bass
quelle
Stellen Sie also sicher, dass der einzige funktionierende öffentliche Konstruktor des Objekts eine Factory benötigt und der erforderliche Factory-Parameter nur durch Aufrufen der statischen Methode der Factory instanziiert werden kann? Scheint eine funktionierende Lösung zu sein, aber ich habe das Gefühl, dass Snippets wie public static IBusinessObject New(BusinessFactory factory) viele Augenbrauen hochziehen, wenn jemand anderes den Code beibehält.
Flater
@ Flater vereinbart. Ich würde den Parameternamen factoryin ändern asFriend. In meiner Codebasis würde es dann alspublic static IBusinessObject New(BusinessFactory asFriend)
Reuven Bass
Ich verstehe es überhaupt nicht. Wie funktioniert es? Die Factory kann keine neuen Instanzen von IBusinessObject erstellen und hat auch keine Absicht (Methoden), dies zu tun ...?
Lapsus
2
    public class HandlerFactory: Handler
    {
        public IHandler GetHandler()
        {
            return base.CreateMe();
        }
    }

    public interface IHandler
    {
        void DoWork();
    }

    public class Handler : IHandler
    {
        public void DoWork()
        {
            Console.WriteLine("hander doing work");
        }

        protected IHandler CreateMe()
        {
            return new Handler();
        }

        protected Handler(){}
    }

    public static void Main(string[] args)
    {
        // Handler handler = new Handler();         - this will error out!
        var factory = new HandlerFactory();
        var handler = factory.GetHandler();

        handler.DoWork();           // this works!
    }
lin
quelle
Bitte versuchen Sie meinen Ansatz: Die 'Factory' ist der Container des Handlers, und nur er kann eine Instanz für sich selbst erstellen. Mit einigen Änderungen kann es Ihren Anforderungen entsprechen.
Lin
1
Wäre in Ihrem Beispiel Folgendes nicht möglich (was keinen Sinn ergibt)?: Factory.DoWork ();
Whyser
1

Ich würde die Factory in dieselbe Assembly wie die Domänenklasse stellen und den internen Konstruktor der Domänenklasse markieren. Auf diese Weise kann jede Klasse in Ihrer Domäne möglicherweise eine Instanz erstellen, aber Sie vertrauen darauf, dass Sie dies nicht tun, oder? Jeder, der Code außerhalb der Domänenschicht schreibt, muss Ihre Factory verwenden.

public class Person
{
  internal Person()
  {
  }
}

public class PersonFactory
{
  public Person Create()
  {
    return new Person();
  }  
}

Allerdings muss ich deinen Ansatz in Frage stellen :-)

Ich denke, wenn Sie möchten, dass Ihre Person-Klasse bei der Erstellung gültig ist, müssen Sie den Code in den Konstruktor einfügen.

public class Person
{
  public Person(string firstName, string lastName)
  {
    FirstName = firstName;
    LastName = lastName;
    Validate();
  }
}
Peter Morris
quelle
1

Diese Lösung basiert auf der Idee von Munificents , ein Token im Konstruktor zu verwenden. Stellen Sie in dieser Antwort sicher, dass das Objekt nur vom Werk erstellt wurde (C #).

  public class BusinessObject
    {
        public BusinessObject(object instantiator)
        {
            if (instantiator.GetType() != typeof(Factory))
                throw new ArgumentException("Instantiator class must be Factory");
        }

    }

    public class Factory
    {
        public BusinessObject CreateBusinessObject()
        {
            return new BusinessObject(this);
        }
    }
Lindhard
quelle
1

Es wurden mehrere Ansätze mit unterschiedlichen Kompromissen erwähnt.

  • Durch das Verschachteln der Factory-Klasse in der privat erstellten Klasse kann die Factory nur 1 Klasse erstellen. An diesem Punkt sind Sie mit einer CreateMethode und einem privaten Ctor besser dran .
  • Die Verwendung der Vererbung und eines geschützten Ctors hat das gleiche Problem.

Ich möchte die Factory als Teilklasse vorschlagen, die private verschachtelte Klassen mit öffentlichen Konstruktoren enthält. Sie verstecken das Objekt, das Ihre Fabrik erstellt, zu 100% und legen nur das, was Sie auswählen, über eine oder mehrere Schnittstellen offen.

Der Anwendungsfall, den ich dafür gehört habe, wäre, wenn Sie 100% der Instanzen in der Fabrik verfolgen möchten. Dieses Design garantiert niemandem, aber die Fabrik hat Zugriff auf die Erstellung von Instanzen von "Chemikalien", die in der "Fabrik" definiert sind, und macht eine separate Baugruppe überflüssig, um dies zu erreichen.

== ChemicalFactory.cs ==
partial class ChemicalFactory {
    private  ChemicalFactory() {}

    public interface IChemical {
        int AtomicNumber { get; }
    }

    public static IChemical CreateOxygen() {
        return new Oxygen();
    }
}


== Oxygen.cs ==
partial class ChemicalFactory {
    private class Oxygen : IChemical {
        public Oxygen() {
            AtomicNumber = 8;
        }
        public int AtomicNumber { get; }
    }
}



== Program.cs ==
class Program {
    static void Main(string[] args) {
        var ox = ChemicalFactory.CreateOxygen();
        Console.WriteLine(ox.AtomicNumber);
    }
}
ubershmekel
quelle
0

Ich verstehe nicht, warum Sie die "Geschäftslogik" vom "Geschäftsobjekt" trennen wollen. Dies klingt nach einer Verzerrung der Objektorientierung, und Sie werden sich auf diese Weise in Knoten binden.

Jim Arnold
quelle
Ich verstehe diesen Unterschied auch nicht. Kann jemand erklären, warum dies getan wird und welcher Code sich im Geschäftsobjekt befinden würde?
pipTheGeek
Es ist nur ein Ansatz, der verwendet werden könnte. Ich möchte, dass Geschäftsobjekte hauptsächlich Datenstrukturen sind, aber nicht das gesamte Feld zum Lesen / Schreiben geöffnet ist. Aber die Frage war wirklich, ob man ein Objekt nur mit Hilfe einer Factory-Methode und nicht direkt instanziieren kann.
Benutzer
@ Jim: Ich persönlich stimme Ihrem Standpunkt zu, aber beruflich wird dies ständig getan. Mein aktuelles Projekt ist ein Webservice, der eine HTML-Zeichenfolge verwendet, sie gemäß einigen voreingestellten Regeln bereinigt und dann die bereinigte Zeichenfolge zurückgibt. Ich muss 7 Ebenen meines eigenen Codes durchgehen, "weil alle unsere Projekte aus derselben Projektvorlage erstellt werden müssen". Es liegt auf der Hand, dass die meisten Leute, die diese Fragen zu SO stellen, nicht die Freiheit haben, den gesamten Code-Fluss zu ändern.
Flater
0

Ich glaube nicht, dass es eine Lösung gibt, die nicht schlimmer ist als das Problem. Alles, was er oben erwähnt, erfordert eine öffentliche statische Fabrik, die meiner Meinung nach ein schlimmeres Problem ist und die Leute nicht davon abhält, nur die Fabrik anzurufen, um Ihr Objekt zu verwenden - sie verbirgt nichts. Stellen Sie am besten eine Schnittstelle bereit und / oder behalten Sie den Konstruktor als intern bei, wenn Sie können. Dies ist der beste Schutz, da die Assembly vertrauenswürdiger Code ist.

Eine Möglichkeit besteht darin, einen statischen Konstruktor zu haben, der eine Fabrik irgendwo mit so etwas wie einem IOC-Container registriert.

user1496062
quelle
0

Hier ist eine andere Lösung im Sinne von "Nur weil du kannst, heißt das nicht, dass du es solltest" ...

Es erfüllt die Anforderungen, den Geschäftsobjektkonstruktor privat zu halten und die Factory-Logik in eine andere Klasse zu stellen. Danach wird es etwas lückenhaft.

Die Factory-Klasse verfügt über eine statische Methode zum Erstellen von Geschäftsobjekten. Es wird von der Geschäftsobjektklasse abgeleitet, um auf eine statisch geschützte Konstruktionsmethode zuzugreifen, die den privaten Konstruktor aufruft.

Die Factory ist abstrakt, sodass Sie keine Instanz davon erstellen können (da es sich auch um ein Geschäftsobjekt handelt, was seltsam wäre). Sie verfügt über einen privaten Konstruktor, sodass Client-Code nicht daraus abgeleitet werden kann.

Was nicht verhindert wird, ist, dass Clientcode auch von der Geschäftsobjektklasse abgeleitet wird und die geschützte (aber nicht validierte) statische Konstruktionsmethode aufruft. Oder schlimmer noch, wir mussten den geschützten Standardkonstruktor aufrufen, den wir hinzufügen mussten, damit die Factory-Klasse überhaupt kompiliert werden konnte. (Was im Übrigen wahrscheinlich ein Problem mit einem Muster darstellt, das die Factory-Klasse von der Business-Objektklasse trennt.)

Ich versuche nicht vorzuschlagen, dass jemand, der bei klarem Verstand ist, so etwas tun sollte, aber es war eine interessante Übung. FWIW, meine bevorzugte Lösung wäre, einen internen Konstruktor und die Baugruppengrenze als Schutz zu verwenden.

using System;

public class MyBusinessObjectClass
{
    public string MyProperty { get; private set; }

    private MyBusinessObjectClass(string myProperty)
    {
        MyProperty = myProperty;
    }

    // Need accesible default constructor, or else MyBusinessObjectFactory declaration will generate:
    // error CS0122: 'MyBusinessObjectClass.MyBusinessObjectClass(string)' is inaccessible due to its protection level
    protected MyBusinessObjectClass()
    {
    }

    protected static MyBusinessObjectClass Construct(string myProperty)
    {
        return new MyBusinessObjectClass(myProperty);
    }
}

public abstract class MyBusinessObjectFactory : MyBusinessObjectClass
{
    public static MyBusinessObjectClass CreateBusinessObject(string myProperty)
    {
        // Perform some check on myProperty

        if (true /* check is okay */)
            return Construct(myProperty);

        return null;
    }

    private MyBusinessObjectFactory()
    {
    }
}
yoyo
quelle
0

Würde mich freuen, einige Gedanken zu dieser Lösung zu hören. Der einzige, der 'MyClassPrivilegeKey' erstellen kann, ist die Factory. und 'MyClass' erfordert es im Konstruktor. So vermeiden Sie Überlegungen zu privaten Auftragnehmern / "Registrierung" in der Fabrik.

public static class Runnable
{
    public static void Run()
    {
        MyClass myClass = MyClassPrivilegeKey.MyClassFactory.GetInstance();
    }
}

public abstract class MyClass
{
    public MyClass(MyClassPrivilegeKey key) { }
}

public class MyClassA : MyClass
{
    public MyClassA(MyClassPrivilegeKey key) : base(key) { }
}

public class MyClassB : MyClass
{
    public MyClassB(MyClassPrivilegeKey key) : base(key) { }
}


public class MyClassPrivilegeKey
{
    private MyClassPrivilegeKey()
    {
    }

    public static class MyClassFactory
    {
        private static MyClassPrivilegeKey key = new MyClassPrivilegeKey();

        public static MyClass GetInstance()
        {
            if (/* some things == */true)
            {
                return new MyClassA(key);
            }
            else
            {
                return new MyClassB(key);
            }
        }
    }
}
Ronen Glants
quelle