Wzorzec fabryki w C #: Jak upewnić się, że wystąpienie obiektu może zostać utworzone tylko przez klasę fabryczną?


94

Ostatnio myślałem o zabezpieczeniu części mojego kodu. Ciekaw jestem, jak można się upewnić, że obiekt nigdy nie zostanie utworzony bezpośrednio, ale tylko za pomocą jakiejś metody klasy fabrycznej. Powiedzmy, że mam klasę „obiektów biznesowych” i chcę się upewnić, że każda instancja tej klasy będzie miała prawidłowy stan wewnętrzny. Aby to osiągnąć, będę musiał sprawdzić przed utworzeniem obiektu, prawdopodobnie w jego konstruktorze. Wszystko jest w porządku, dopóki nie zdecyduję, że chcę, aby ta kontrola była częścią logiki biznesowej. Jak więc ustawić, aby obiekt biznesowy był tworzony tylko za pomocą jakiejś metody w mojej klasie logiki biznesowej, ale nigdy bezpośrednio? Pierwsza naturalna chęć użycia dobrego, starego słowa kluczowego „przyjaciel” języka C ++ nie powiedzie się w przypadku języka C #. Potrzebujemy więc innych opcji ...

Spróbujmy na przykład:

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;
    }
}

Wszystko jest w porządku, dopóki nie przypomnisz sobie, że nadal możesz bezpośrednio utworzyć instancję MyBusinessObjectClass, bez sprawdzania danych wejściowych. Chciałbym całkowicie wykluczyć tę techniczną możliwość.

Więc co o tym myśli społeczność?


czy metoda MyBoClass jest błędnie napisana?
pradeeptp

2
Jestem zdezorientowany, dlaczego nie chciałbyś, aby obiekty biznesowe były odpowiedzialne za ochronę własnych niezmienników? Celem wzorca fabryki (jak rozumiem) jest albo A) poradzenie sobie z tworzeniem złożonej klasy z wieloma zależnościami lub B) niech fabryka będzie decydowała, jaki typ środowiska uruchomieniowego ma zwrócić, ujawniając tylko interfejs / streszczenie klasa do klienta. Umieszczanie reguł biznesowych w obiektach biznesowych jest samą ISTOTĄ OOP.
sara

@kai Prawda, obiekt biznesowy jest odpowiedzialny za ochronę swojego niezmiennego, ale obiekt wywołujący konstruktora musi upewnić się, że argumenty są prawidłowe przed przekazaniem ich do konstruktora ! Celem CreateBusinessObjectmetody jest sprawdzenie poprawności argumentów ORAZ (zwrócenie nowego prawidłowego obiektu LUB zwrócenie błędu) w pojedynczym wywołaniu metody, gdy obiekt wywołujący nie wie od razu, czy argumenty są prawidłowe do przekazania do konstruktora.
Lightman,

2
Nie zgadzam się. Konstruktor jest odpowiedzialny za wykonanie wszelkich wymaganych kontroli podanych parametrów. Jeśli wywołuję konstruktora i nie zostanie zgłoszony żaden wyjątek, mam nadzieję, że utworzony obiekt jest w prawidłowym stanie. Nie powinno być fizycznie możliwe utworzenie instancji klasy przy użyciu „niewłaściwych” argumentów. Utworzenie Distanceinstancji z ujemnym argumentem powinno ArgumentOutOfRangeExceptionna przykład spowodować , że nic nie zyskasz na utworzeniu instancji, DistanceFactoryktóra wykonuje to samo sprawdzenie. Nadal nie widzę, co możesz tutaj zyskać.
sara

Odpowiedzi:


55

Wygląda na to, że przed utworzeniem obiektu chcesz po prostu uruchomić logikę biznesową - dlaczego więc nie utworzyć statycznej metody wewnątrz klasy „BusinessClass”, która wykonuje wszystkie brudne sprawdzanie właściwości „myProperty” i uczyni konstruktor prywatnym?

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;
    }
}

Nazywanie tego byłoby całkiem proste:

BusinessClass objBusiness = BusinessClass.CreateObject(someProperty);

6
Ach, możesz również zgłosić wyjątek zamiast zwracać wartość null.
Ricardo Nolde

5
nie ma sensu mieć prywatnego konstruktora domyślnego, jeśli zadeklarowałeś konstruktora niestandardowego. również w tym przypadku nie ma dosłownie nic do zyskania używając statycznego „konstruktora” zamiast po prostu wykonać walidację w rzeczywistym konstruktorze (ponieważ i tak jest on częścią klasy). jest nawet gorzej, ponieważ możesz teraz uzyskać zerową wartość zwracaną, otwierając się na NRE i "co do cholery oznacza to zero?" pytania.
sara

Czy jest jakiś dobry sposób, aby wymagać tej architektury za pomocą interfejsu / abstrakcyjnej klasy bazowej? tj. interfejs / abstrakcyjna klasa bazowa, która mówi, że implementatorzy nie powinni ujawniać ctora.
Arash Motamedi

1
@Arash Nie. Interfejsy nie mogą definiować konstruktorów, a klasa abstrakcyjna, która definiuje chroniony lub wewnętrzny konstruktor, nie może uniemożliwić dziedziczeniu klas przed ujawnieniem go przez własny konstruktor publiczny.
Ricardo Nolde

66

Możesz ustawić konstruktor jako prywatny, a fabrykę jako typ zagnieżdżony:

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

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

Działa to, ponieważ typy zagnieżdżone mają dostęp do prywatnych elementów członkowskich ich otaczających typów. Wiem, że to trochę restrykcyjne, ale mam nadzieję, że pomoże ...


Zastanawiałem się nad tym, ale skutecznie przenosi te sprawdzenia do samego obiektu biznesowego, czego staram się uniknąć.
Użytkownik

@ so-tester: Nadal możesz mieć kontrole w fabryce zamiast typu BusinessObject.
Jon Skeet

3
@JonSkeet Wiem, że to pytanie jest naprawdę stare, ale jestem ciekawy, jaka jest korzyść z umieszczenia CreateBusinessObjectmetody wewnątrz Factoryklasy zagnieżdżonej zamiast używania tej metody statycznej jako metody bezpośrednio z BusinessObjectklasy ... czy możesz sobie przypomnieć motywacja do zrobienia tego?
Kiley Naro,

3
@KileyNaro: Cóż, o to właśnie chodziło w tym pytaniu :) Niekoniecznie daje to wiele korzyści, ale odpowiada na pytanie ... mogą być jednak chwile, kiedy jest to przydatne - przychodzi na myśl wzór budowniczego. (W takim przypadku budowniczy byłby klasą zagnieżdżoną i miałby metodę instancji o nazwie Build.)
Jon Skeet,

53

Lub, jeśli chcesz naprawdę zaszaleć, odwróć kontrolę: niech klasa zwróci fabrykę i oprzyrząduje fabrykę delegatem, który może utworzyć klasę.

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;
  }
}

:)


Bardzo fajny fragment kodu. Logika tworzenia obiektów znajduje się w osobnej klasie, a obiekt można utworzyć tylko przy użyciu fabryki.
Nikolai Samteladze

Fajne. Czy jest jakiś sposób, aby ograniczyć tworzenie BusinessObjectFactory do statycznego BusinessObject.GetFactory?
Felix Keil

1
Jaka jest zaleta tej inwersji?
yatskovsky

1
@yatskovsky To tylko sposób na zapewnienie, że obiekt może zostać utworzony tylko w kontrolowany sposób, a jednocześnie jest w stanie uwzględnić logikę tworzenia w dedykowanej klasie.
Fabian Schmied

15

Można utworzyć konstruktora klasy MyBusinessObjectClass jako wewnętrzną i przenieść go wraz z fabryką do ich własnego zestawu. Teraz tylko fabryka powinna mieć możliwość skonstruowania instancji klasy.


7

Oprócz tego, co zasugerował Jon, możesz również mieć metodę fabryczną (w tym check) jako metodę statyczną BusinessObject w pierwszej kolejności. Następnie ustaw konstruktora jako prywatny, a wszyscy inni będą zmuszeni użyć metody statycznej.

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

Ale prawdziwe pytanie brzmi - dlaczego masz to wymaganie? Czy można przenieść fabrykę lub metodę fabryczną do klasy?


To nie jest wymóg. Po prostu chcę mieć czyste oddzielenie obiektu biznesowego i logiki. Tak jak niewłaściwe jest umieszczanie tych kontroli w kodzie stron, uważam za niewłaściwe przeprowadzanie tych kontroli w samych obiektach. Cóż, może podstawowa walidacja, ale nie reguły biznesowe.
Użytkownik

Jeśli chcesz oddzielić logikę i strukturę klasy tylko dla wygody, zawsze możesz użyć częściowej klasy i mieć obie w oddzielnych plikach ...
Grx70

7

Po tylu latach zadano to pytanie, a wszystkie odpowiedzi, które widzę, niestety mówią ci, jak powinieneś zrobić swój kod, zamiast udzielać prostej odpowiedzi. Rzeczywista odpowiedź, której szukałeś, to posiadanie klas z prywatnym konstruktorem, ale publicznym instancją, co oznacza, że ​​możesz tworzyć nowe instancje tylko z innych istniejących instancji ... które są dostępne tylko w fabryce:

Interfejs dla twoich zajęć:

public interface FactoryObject
{
    FactoryObject Instantiate();
}

Twoja klasa:

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

    private YourClass() {}

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

I wreszcie fabryka:

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();
    }
}

Następnie możesz łatwo zmodyfikować ten kod, jeśli potrzebujesz dodatkowych parametrów do tworzenia instancji lub do wstępnego przetworzenia utworzonych instancji. Ten kod pozwoli ci wymusić tworzenie instancji przez fabrykę, ponieważ konstruktor klasy jest prywatny.


4

Jeszcze inną (uproszczoną) opcją jest utworzenie statycznej metody fabryki w klasie BusinessObject i zachowanie prywatności konstruktora.

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

    private BusinessObject()
    {
    }
}

2

Więc wygląda na to, że to, czego chcę, nie może być zrobione w „czysty” sposób. Jest to zawsze coś w rodzaju „oddzwonienia” do klasy logiki.

Może mógłbym to zrobić w prosty sposób, po prostu zrobić metodę contructor w klasie obiektów, najpierw wywołać klasę logiki, aby sprawdzić dane wejściowe?

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;
    }
}

W ten sposób obiekt biznesowy nie jest bezpośrednio tworzony, a metoda sprawdzania publicznego w logice biznesowej również nie zaszkodzi.


2

W przypadku dobrej separacji między interfejsami i implementacjami
wzorzec protected-constructor-public-initializer umożliwia bardzo zgrabne rozwiązanie.

Biorąc pod uwagę obiekt biznesowy:

public interface IBusinessObject { }

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

    protected BusinessObject() 
    { ... }
}

i fabryka biznesowa:

public interface IBusinessFactory { }

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

    protected BusinessFactory() 
    { ... }
}

następująca zmiana na BusinessObject.New() inicjatorze daje rozwiązanie:

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

    ...
}

Tutaj do wywołania BusinessObject.New()inicjatora potrzebne jest odniesienie do konkretnej fabryki biznesowej . Ale jedyną osobą, która ma wymagane referencje, jest sama fabryka.

Mamy to, czego chcieliśmy: jedyny, który może tworzyć, BusinessObjectto BusinessFactory.


Więc upewniasz się, że jedyny działający konstruktor publiczny obiektu wymaga Factory, a tego wymaganego parametru Factory można utworzyć tylko przez wywołanie metody statycznej Factory? Wydaje się, że to działające rozwiązanie, ale mam wrażenie, że takie fragmenty public static IBusinessObject New(BusinessFactory factory) uniosą wiele brwi, jeśli ktoś inny zajmie się kodem.
Flater

@Flater uzgodniony. Zmieniłbym nazwę parametru factoryna asFriend. W mojej bazie kodu wyglądałoby to następującopublic static IBusinessObject New(BusinessFactory asFriend)
Reuven Bass

W ogóle tego nie rozumiem. Jak to działa? Fabryka nie może tworzyć nowych instancji IBusinessObject ani nie ma zamiaru (metod) tego robić ...?
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!
    }

Wypróbuj moje podejście. „Fabryka” jest kontenerem modułu obsługi i tylko ona może utworzyć dla siebie instancję. z pewnymi modyfikacjami może pasować do twoich potrzeb.
lin

1
W twoim przykładzie, czy nie byłoby możliwe (co nie miałoby sensu) ?: factory.DoWork ();
Whyser

1

Umieściłbym fabrykę w tym samym zestawie co klasa domeny i oznaczyłem konstruktora klasy domeny jako wewnętrzny. W ten sposób każda klasa w Twojej domenie może być w stanie utworzyć instancję, ale Ty sobie nie ufasz, prawda? Każdy, kto pisze kod poza warstwą domeny, będzie musiał korzystać z Twojej fabryki.

public class Person
{
  internal Person()
  {
  }
}

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

Muszę jednak zakwestionować Twoje podejście :-)

Myślę, że jeśli chcesz, aby Twoja klasa Person była ważna podczas tworzenia, musisz umieścić kod w konstruktorze.

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

1

Rozwiązanie to opiera się na wspaniałej idei wykorzystania tokena w konstruktorze. Sporządzono w tej odpowiedzi, upewnij się, że obiekt został utworzony tylko przez fabrykę (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);
        }
    }

1

Wspomniano o wielu podejściach z różnymi kompromisami.

  • Zagnieżdżenie klasy fabryki w klasie zbudowanej prywatnie pozwala fabryce zbudować tylko 1 klasę. W tym momencie lepiej będzie, jeśli skorzystasz z Createmetody i prywatnego lekarza.
  • Korzystanie z dziedziczenia i chronionego kontrolera ma ten sam problem.

Chciałbym zaproponować fabrykę jako klasę częściową, która zawiera prywatne klasy zagnieżdżone z publicznymi konstruktorami. W 100% ukrywasz obiekt, który tworzy Twoja fabryka, i ujawniasz tylko to, co wybierzesz, za pośrednictwem jednego lub wielu interfejsów.

Przypadek użycia, o którym słyszałem, to przypadek, gdy chcesz śledzić 100% instancji w fabryce. Taka konstrukcja gwarantuje tylko fabryce dostęp do tworzenia instancji „chemikaliów” zdefiniowanych w „fabryce” i eliminuje potrzebę oddzielnego montażu, aby to osiągnąć.

== 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);
    }
}

0

Nie rozumiem, dlaczego chcesz oddzielić „logikę biznesową” od „obiektu biznesowego”. Brzmi to jak zniekształcenie orientacji obiektu, a skończysz na zawiązywaniu się węzłami, stosując takie podejście.


Nie rozumiem też tego rozróżnienia. Czy ktoś może wyjaśnić, dlaczego tak się dzieje i jaki kod znajdowałby się w obiekcie biznesowym?
pipTheGeek

To tylko jedno podejście, które można zastosować. Chcę, aby obiekt biznesowy składał się głównie ze struktur danych, ale nie z całym polem otwartym do odczytu / zapisu. Ale tak naprawdę chodziło o możliwość utworzenia instancji obiektu tylko za pomocą metody fabrycznej, a nie bezpośrednio.
Użytkownik

@Jim: Osobiście zgadzam się z twoim punktem widzenia, ale profesjonalnie jest to robione stale. Mój obecny projekt to usługa internetowa, która pobiera ciąg znaków HTML, czyści go zgodnie z pewnymi ustalonymi regułami, a następnie zwraca wyczyszczony ciąg. Muszę przejść przez 7 warstw mojego własnego kodu, „ponieważ wszystkie nasze projekty muszą być zbudowane z tego samego szablonu projektu”. Jest zrozumiałe, że większość ludzi zadających te pytania na SO nie ma swobody zmiany całego przepływu swojego kodu.
Flater

0

Nie sądzę, aby było rozwiązanie, które nie jest gorsze niż problem, wszystko, co powyżej, wymaga publicznej fabryki statycznej, której IMHO jest gorszym problemem i nie powstrzyma ludzi po prostu dzwoniąc do fabryki, aby użyć twojego obiektu - to niczego nie ukrywa. Najlepiej ujawnić interfejs i / lub zachować konstruktora jako wewnętrznego, jeśli to możliwe, jest to najlepsza ochrona, ponieważ zestaw jest zaufanym kodem.

Jedną z opcji jest posiadanie statycznego konstruktora, który rejestruje fabrykę gdzieś w czymś w rodzaju kontenera IOC.


0

Oto inne rozwiązanie w duchu „tylko dlatego, że możesz, nie znaczy, że powinieneś” ...

Spełnia on wymagania zachowania prywatności konstruktora obiektów biznesowych i umieszczenia logiki fabryki w innej klasie. Potem robi się trochę szkicowo.

Klasa fabryczna zawiera statyczną metodę tworzenia obiektów biznesowych. Wywodzi się z klasy obiektu biznesowego w celu uzyskania dostępu do statycznej metody konstrukcji chronionej, która wywołuje konstruktor prywatny.

Fabryka jest abstrakcyjna, więc nie można w rzeczywistości utworzyć jej instancji (ponieważ byłby to również obiekt biznesowy, więc byłoby to dziwne) i ma prywatny konstruktor, więc kod klienta nie może z niej pochodzić.

Nie zapobiega się temu, że kod klienta również pochodzi z klasy obiektu biznesowego i wywołuje chronioną (ale nie zweryfikowaną) statyczną metodę konstrukcji. Lub, co gorsza, wywołanie chronionego domyślnego konstruktora, który musieliśmy dodać, aby skompilować klasę fabryczną w pierwszej kolejności. (Co, nawiasem mówiąc, może być problemem w przypadku dowolnego wzorca oddzielającego klasę fabryki od klasy obiektu biznesowego).

Nie próbuję sugerować nikomu przy zdrowych zmysłach, żeby zrobił coś takiego, ale było to interesujące ćwiczenie. FWIW, moim preferowanym rozwiązaniem byłoby użycie wewnętrznego konstruktora i granicy montażu jako osłony.

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()
    {
    }
}

0

Byłbym wdzięczny za przemyślenia na temat tego rozwiązania. Jedynym, który może utworzyć „MyClassPrivilegeKey”, jest fabryka. a „MyClass” wymaga tego w konstruktorze. Uniknięto w ten sposób refleksji na temat prywatnych kontrahentów / „rejestracji” w fabryce.

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);
            }
        }
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.