Моя версия, которая включает в себя цепочку, где находится разница, и в чем разница.
function DeepObjectCompare(O1, O2)
{
try {
DOC_Val(O1, O2, ['O1->O2', O1, O2]);
return DOC_Val(O2, O1, ['O2->O1', O1, O2]);
} catch(e) {
console.log(e.Chain);
throw(e);
}
}
function DOC_Error(Reason, Chain, Val1, Val2)
{
this.Reason=Reason;
this.Chain=Chain;
this.Val1=Val1;
this.Val2=Val2;
}
function DOC_Val(Val1, Val2, Chain)
{
function DoThrow(Reason, NewChain) { throw(new DOC_Error(Reason, NewChain!==undefined ? NewChain : Chain, Val1, Val2)); }
if(typeof(Val1)!==typeof(Val2))
return DoThrow('Type Mismatch');
if(Val1===null || Val1===undefined)
return Val1!==Val2 ? DoThrow('Null/undefined mismatch') : true;
if(Val1.constructor!==Val2.constructor)
return DoThrow('Constructor mismatch');
switch(typeof(Val1))
{
case 'object':
for(var m in Val1)
{
if(!Val1.hasOwnProperty(m))
continue;
var CurChain=Chain.concat([m]);
if(!Val2.hasOwnProperty(m))
return DoThrow('Val2 missing property', CurChain);
DOC_Val(Val1[m], Val2[m], CurChain);
}
return true;
case 'number':
if(Number.isNaN(Val1))
return !Number.isNaN(Val2) ? DoThrow('NaN mismatch') : true;
case 'string':
case 'boolean':
return Val1!==Val2 ? DoThrow('Value mismatch') : true;
case 'function':
if(Val1.prototype!==Val2.prototype)
return DoThrow('Prototype mismatch');
if(Val1!==Val2)
return DoThrow('Function mismatch');
return true;
default:
return DoThrow('Val1 is unknown type');
}
}
Классический синглтон также имеет статический метод getInstance () - вызовите его через отражение вместо использования newInstance (). Да, это больше работы, но так оно и есть ...
Или вы могли бы использовать setAccessible () для вызова частного конструктора в любом случае, но вы сломаете синглтон и отправитесь к черту.
В-третьих, вы могли бы вообще отказаться от синглтона и найти лучшее решение (обычно оно есть).
Вы можете использовать отражение, чтобы получить ссылку на статический фабричный метод класса, а затем вызвать его. Заводской метод может привести к принудительному применению одноэлементного шаблона
Class c = Class.forName("test.MyClass");
Method factoryMethod = c.getDeclaredMethod("getInstance");
Object singleton = factoryMethod.invoke(null, null);
А затем
public class MyClass {
private static MyClass instance;
private MyClass() {
// private c'tor
}
public static synchronized MyClass getInstance() {
if (instance == null) {
instance = new MyClass();
}
return instance:
}
}
Предупреждение: одноэлементный шаблон проектирования может нанести ущерб вашему здоровью в долгосрочной перспективе.
Вы должны знать имя метода вашего одноэлементного класса, который создает объект. Если он, например, называется instance (), вы можете сделать что-то вроде
Class c = Class.forName("MySingleton");
Method m = c.getDeclaredMethod("instance",null);
MySingleton singleton = m.invoke(null,null);
Вы можете использовать перечисление и иметь HashMap, названный для каждого из них. Или вы можете использовать некоторую структуру внедрения зависимостей, чтобы получить синглтон чего-то (Guice?)
Edit Хорошо, я тоже перейду к примеру кода:
package tests;
public class EnumSingleton {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("tests.Singleton1");
Operation instance = Operation.class.cast(c.getEnumConstants()[0]);
System.out.println(instance.getTHEAnswer());
c = Class.forName("tests.Singleton2");
instance = Operation.class.cast(c.getEnumConstants()[0]);
System.out.println(instance.getTHEAnswer());
}
}
interface Operation {
int getTHEAnswer();
}
enum Singleton1 implements Operation {
INSTANCE;
@Override
public int getTHEAnswer() {
return 42;
}
}
enum Singleton2 implements Operation {
INSTANCE;
@Override
public int getTHEAnswer() {
return 84;
}
}
И он в целости и сохранности. Править И теперь имеет какое-то значение.
. Можете ли вы вместо этого полагаться на существование статического фабричного метода? Он будет полагаться на какое-то соглашение об именах, но это сработает.
Итог: если известно, что класс является одноэлементным, то он должен нести ответственность за эту политику. Следовательно, он должен соответствовать некоторой реализации шаблона, и если вы хотите динамический выбор среди таких классов, вам необходимо иметь возможность предсказать подход «Factory».