//Rozdział 4.
//Fabryki


public class Point
{
    private double x, y;
    public Point(double x, double y)
    {
        this.x = x;
        this.y = y;
    }
}

Point(float r, float theta)
{
    x = r * Math.Cos(theta);
    y = r * Math.Sin(theta);
}

public enum CoordinateSystem
{
    Cartesian,
    Polar
}

public Point(double a,
    double b, //nazwy nie przekazują zamiaru
    CoordinateSystem cs = CoordinateSystem.Cartesian)
{
switch (cs)
{
    case CoordinateSystem.Polar:
        x = a * Math.Cos(b);
        y = a * Math.Sin(b);
        break;
    default:
        x = a;
        y = b;
        break;
    }
}

//Metoda Fabrykująca
public class Point
{
    protected Point(double x, double y)
    {
        this.x = x;
        this.y = y;
    }
    public static Point NewCartesianPoint(double x, double y)
    {
        return new Point(x, y);
    }
    public static Point NewPolarPoint(double rho, double theta)
    {
        return new Point(rho*Math.Cos(theta), rho*Math.Sin(theta));
    }
    //inne składowe pominięto
}
var point = Point.NewPolarPoint(5, Math.PI / 4);

class PointFactory
{
    public static Point NewCartesianPoint(float x, float y)
    {
        return new Point(x, y); //musi być publiczny
    }
    //to samo dla NewPolarPoint
}

var myPoint = PointFactory.NewCartesian(3, 4);
public class Point
{
    //tutaj typowe składowe
    //zauważmy, że konstruktor jest ponownie prywatny
    private Point(double x, double y) { ... }
    public static class Factory
    {
       public static Point NewCartesianPoint(double x, double y)
       {
            return new Point(x, y); //użycie prywatnego konstruktora
       }
       //podobnie dla NewPolarPoint()
    }
}
var point = Point.Factory.NewCartesianPoint(2, 3);

//Separacja logiczna
public partial class Point { ... }
public partial class Point
{
    public static class Factory
    {
        //jak wcześniej
    }
}

//Fabryka Abstrakcyjna
public interface IHotDrink
{
    void Consume();
}
internal class Tea : IHotDrink
{
    public void Consume()
    {
        Console.WriteLine("Ta herbata jest dobra, ale wolałbym ją z mlekiem.");
    }
}
internal class CoffeeFactory
{
    public IHotDrink Prepare(int amount)
    {
        Console.WriteLine($"Zmiel trochę ziaren, zagotuj wodę, zalej {amount} ml, dodaj śmietanę i cukier. Smacznego!");
        return new Coffee();
    }
}
public IHotDrink MakeDrink(string type)
{
    switch (type)
    {
        case "tea":
            return new TeaFactory().Prepare(200);
        case "coffee":
            return new CoffeeFactory().Prepare(50);
        default:
            throw new ArgumentException("type");
    }
}
public interface IHotDrinkFactory
{
    IHotDrink Prepare(int amount);
}

internal class CoffeeFactory : IHotDrinkFactory
{
    public IHotDrink Prepare(int amount)
    {
        Console.WriteLine($"Zmiel trochę ziaren, zagotuj wodę, zalej {amount} ml, dodaj śmietanę i cukier. Smacznego!");
        return new Coffee();
    }
}

//Fabryka Funkcyjna
type ICountryInfo =
    abstract member Capital : string
type Country =
    | USA
    | UK
let make country =
    match country with
    | USA -> {new ICountryInfo with
              member x.Capital = "Waszyngton" }
    | UK ->  {new ICountryInfo with
              member x.Capital = "Londyn" }
type Country =
    | USA
    | UK
with
    static member Create = function
        | "USA" | "Ameryka" -> USA
        | "UK" | "Anglia" -> UK
        | _ -> failwith "Nie ma takiego kraju"
let usa = Country.Create "Ameryka"
 
