Stessa firma del costruttore con semantica diversa?

4

Occasionalmente, mi trovo di fronte al problema che devo fornire alcuni costruttori per inizializzare oggetti con set di dati diversi. Questi set possono essere trasformati matematicamente l'uno nell'altro, ad esempio:

        public Element(int x, int y, int width, int height)
        {
            setValues(width, height, x, y);
        }

        public Element(int RectTop, int RectLeft, int RectRight, int RectBottom)
        {
            setValues(RectRight - RectLeft, Math.Abs(RectBottom - RectTop), RectLeft, -RectTop);
        }

Con un tale sovraccarico desiderato, che in realtà non è uno a causa delle stesse firme, è probabile che venga invocato il costruttore errato, ad es. fornisci i valori Rect e viene invocato il primo costruttore. Supponendo, avremmo diversi tipi, potremmo realizzarlo scambiando i tipi con una buona conoscenza che questa è una cattiva pratica. Ma questo non è il caso in questo semplice esempio con quattro volte il tipo int.

Riesci a pensare ad alcune alternative eleganti per ottenere il comportamento desiderato che abbiamo la comodità di sovraccaricare, ma con la stessa firma?

    
posta McMannus 05.06.2013 - 19:36
fonte

2 risposte

12
public Element(Point topLeft, Size size)

public Element(Point topLeft, Point bottomRight)

Potresti anche usare i metodi di fabbrica

public static Element FromLeftTopWidthSize(int left, int top, int width, int height)

public static Element FromLeftTopRightBottom(int left, int top, int right, int bottom)

O puoi usare un'interfaccia fluente

Element.Top(10).Left(28).Right(112).Bottom(101);

Element.Top(10).Left(28).Width(102).Height(73);

Puoi facilmente utilizzare le interfacce per forzare Sinistra dopo Alto, Destro o Largo dopo Sinistra, Inferiore dopo Destra e Altezza dopo Larghezza. (vedi sotto)

Oppure, puoi fare la cosa più sensata di tutte e non permettere le diverse scelte. Basta sceglierne uno e attenersi ad esso.

Esempio di un'interfaccia progressiva fluente:

public class ElementBuilder : ITopSyntax, ILeftSyntax, IRightSyntax, IWidthSyntax
{
    private int _top;
    private int _left;
    private int _right;

    public ElementBuilder(int top)
    {
        _top = top;
    }

    ILeftSyntax ITopSyntax.Left(int left)
    {
        _left = left;
        return this;
    }

    IRightSyntax ILeftSyntax.Right(int right)
    {
        _right = right;
        return this;
    }

    IWidthSyntax ILeftSyntax.Width(int width)
    {
        _right = width + _left;
        return this;
    }

    Element IRightSyntax.Bottom(int bottom)
    {
        return new Element(_left, _top, _right, bottom);
    }

    Element IWidthSyntax.Height(int height)
    {
        return new Element(_left, _top, _right, _top + height);
    }
}

internal interface ILeftSyntax
{
    IRightSyntax Right(int right);
    IWidthSyntax Width(int width);
}

internal interface IRightSyntax
{
    Element Bottom(int bottom);
}

internal interface IWidthSyntax
{
    Element Height(int height);
}

internal interface ITopSyntax
{
    ILeftSyntax Left(int left);
}
    
risposta data 05.06.2013 - 19:48
fonte
1

Un modo per farlo sarebbe scrivere un costruttore che accetta un oggetto Rectangle .

public Element(Rectangle r) { ...

Che puoi chiamare così:

var element = new Element(new Rectangle(x, y, width, height));

Naturalmente, ora sei tornato dove hai iniziato, dal momento che Rectangle richiede gli stessi parametri del primo sovraccarico.

Se ti impegni a fornire un overload di costruttore per la leggera variazione in matematica, puoi semplicemente aggiungere un flag booleano per rendere la firma univoca. Qualcosa come:

public Element(int RectTop, int RectLeft, int RectRight, int RectBottom, bool isRect)

È un po 'un hack, e non importa cosa passi al parametro bool , ma garantisce che venga chiamato un particolare overload.

    
risposta data 05.06.2013 - 19:47
fonte

Leggi altre domande sui tag