Sto cercando consigli su come progettare il seguente scenario:
Ho una classe vettoriale tridimensionale, le voci sono di tipo double
. Voglio che due vettori siano considerati uguali se i loro elementi corrispondenti differiscono di meno di una tolleranza data, ad es. 10 ^. (- 10)
(Gli esempi di codice sono in sintassi C # mescolati con pseudocode.)
public class Vector
{
double x, y, z;
public bool TolEquals(Vector other)
{
return ((|x-other.x| < 10^(-10)) // absolute value of difference
&& (|y-other.y| < 10^(-10)) // must be small enough
&& (|z-other.z| < 10^(-10)));
}
}
Ho bisogno di una certa flessibilità perché altre persone potrebbero voler usare un altro valore di tolleranza di 10 ^ (- 10).
Ciò significa che ho bisogno di incorporare in qualche modo il valore di tolleranza nel mio metodo di controllo di uguaglianza, ad es. così:
public bool TolEquals(Vector other, double tolerance)
{
return ((|x-other.x| < tolerance)
&& (|y-other.y| < tolerance)
&& (|z-other.z| < tolerance);
}
Ma so che durante tutta la mia applicazione, la tolleranza sarà costante. E non voglio scrivere
v1.TolEquals(v2, tolerance)
ogni volta che controllo l'uguaglianza poiché non è molto leggibile e poiché gli errori di battitura possono accadere facilmente.
Un'altra idea è di memorizzare tolerance
come membro della classe Vector
e impostarlo nel costruttore di Vector
. Quindi il metodo TolEquals
richiede solo un argomento:
public class Vector
{
double x, y, z;
double tolerance;
public Vector(double x, double y, double z, double tolerance)
{
this.x = x;
this.y = y;
this.z = z;
this.tolerance = tolerance;
}
public bool TolEquals(Vector other)
{
return ((|x-other.x| < tolerance)
&& (|y-other.y| < tolerance)
&& (|z-other.z| < tolerance));
}
}
Ma poi qualcuno che usa il mio codice potrebbe creare accidentalmente due vettori v
e w
con diverse tolleranze e otterrebbe risposte diverse da v.TolEquals(w)
e w.TolEquals(v)
. Neanche questo dovrebbe accadere.
Probabilmente aggiungerò alcuni metodi statici alla mia classe Vector
prima o poi che prendono diversi vettori come input e eseguono alcuni calcoli che coinvolgono TolEquals
con essi. Quindi devo assicurarmi che tutti i vettori utilizzino lo stesso valore di tolleranza.
Sto cercando una soluzione che mi permetta di definire tolerance
da qualche parte nel programma, al di fuori della classe Vector
, e quindi tutti i vettori usano quel tolerance
per il loro TolEquals
. E voglio essere in grado di cambiare tolerance
in fase di esecuzione (non ne ho bisogno ora ma potrebbe averne bisogno in seguito), risultando invece in tutti i vettori che utilizzano il nuovo valore.
MA potrebbe essere che qualcuno che usa il mio codice abbia bisogno di due diversi valori di tolleranza coesistenti. Avrebbe due distinti gruppi di vettori, utilizzando i diversi valori di tolleranza, ma i gruppi non interagiscono tra loro.
Quindi un campo statico tolerance
non è nemmeno un'opzione.
Scriverò anche una classe matrix
e forse alcuni altri che avranno un analogo di TolEquals
, quindi la soluzione non dovrebbe essere limitata alla mia classe Vector
.
Qualche idea?
Modifica
Ispirato alla risposta accettata, mi sento come presentare la mia soluzione finale (in C #) e inserire alcuni commenti. Tutte le informazioni sono fornite nella risposta accettata, ma mi ci è voluto un po 'di tempo per elaborarle in modo da perfezionarle un po' qui. Ho deciso di implementarlo con un campo statico per la tolleranza nella classe vettoriale generica:
public interface ISpace { double TOL(); }
public class ZeroTolSpace : ISpace { public double TOL() => 0; }
public class Tol10Space : ISpace { public double TOL() => 1E-10; }
// Others can easily write new classes for other tolerance values.
public class Vector<T> where T: ISpace, new()
{
private double x, y, z;
public static double? tol = null; // Changeable at runtime (if really needed)!
public Vector(double x, double y, double z)
{
this.x = x; this.y = y; this.z = z;
tol = tol ?? (new T()).TOL(); // Since tol is static, the new-operator
// gets called only once, minimzing overhead.
}
public bool TolEquals(Vector other)
{
return ((Math.Abs(x-other.x) < tol)
&& (Math.Abs(y-other.y) < tol)
&& (Math.Abs(z-other.z) < tol));
}
}
Quindi nel metodo Main
possiamo fare:
Vector<ZeroTolSpace> v1 = new Vector<ZeroTolSpace> (0, 0, 0);
Vector<ZeroTolSpace> v2 = new Vector<ZeroTolSpace>(1E-11, 1E-11, 1E-11);
v1.TolEquals(v2)); // false since _tol is 0
Vector<Tol10Space> w1 = new Vector<Tol10Space> (0, 0, 0);
Vector<Tol10Space> w2 = new Vector<Tol10Space> (1E-11, 1E-11, 1E-11);
w1.TolEquals(w2)); // true since tol is 1E-10
v1.TolEquals(w2)); // compile error: types do not match
Matrice e altre classi dipendenti dalla tolleranza possono essere implementate allo stesso modo.