У меня есть сложный класс, который требует, чтобы определенные аргументы передавались в конструктор. Однако я предоставляю клиентам упрощенный API.
Мой внутренний класс выглядит примерно так:
class Foo {
constructor(p, c) {
}
foo() {
}
}
Где p
- это внутренняя ссылка, которая недоступна для клиентов .
Поддержка API Publi c
Я хочу разрешить клиентам создавать экземпляры этого класса, но я не хочу, чтобы им требовалась ссылка на частный объект p
, Для пользователей этого API доступ к p
будет трудоемким и нарушит существующий код, поэтому я хочу скрыть его с псевдонимом.
Подклассы для спасения? Почти.
Сначала я просто расширил Foo, спрятал закрытый аргумент (предоставив код для доступа к нему) и выставил его через publi c API:
class PublicFoo extends Foo {
constructor(c) {
// Use internal functions to get "p"
var p;
super(p, c);
}
}
This почти сработало, но я столкнулся с серьезным недостатком. Существуют ситуации, когда клиенту необходимо проверить тип объекта. В зависимости от ситуации Foo может быть создан внутренне с использованием внутреннего класса или клиентом с использованием API publi c.
Если для создания экземпляра Foo использовался API publi c, то внутренний instaceof
проверки работают нормально: publicFoo instanceof Foo
возвращает true
. Но если API создал экземпляр Foo, используя внутренний класс, то публичные c instanceof
проверки не пройдены: internalFoo instanceof PublicFoo
возвращает false
. Клиент может печатать экземпляры чеков, созданные с использованием API publi c, но проверки того же типа не выполняются для экземпляров, созданных внутри (например, с помощью заводских функций).
Этого следует ожидать и имеет смысл для меня , но это ломает мой вариант использования. Я не могу использовать простой подкласс, потому что подкласс не является надежным псевдонимом для внутреннего класса.
var f = new Foo();
f instanceof PublicFoo; // false
Как насчет прокси?
Поэтому я столкнулся с " «умный» набрал высоту и попытался использовать прокси вместо этого, что показалось (известные последние слова) как идеальное решение:
var PublicFoo = new Proxy(Foo, {
construct(target, args) {
// Use internal functions to get "p"
var p;
return new target(p, ...args);
}
});
Я могу выставить Прокси, перехватить вызовы конструктора, предоставить необходимую ссылку на частный объект, и instanceof
не сломан!
var f = new Foo();
f instanceof PublicFoo; // true!!!
Но Прокси нарушает наследование ...
Disaster! Клиенты больше не могут наследовать (свою версию) Foo!
class Bar extends PublicFoo {
constructor(c) {
super(c);
}
bar() {
}
}
Ловушка конструктора Proxy всегда возвращает новый экземпляр Foo
, , а не подкласс Bar
.
Это приводит к ужасным, ужасным проблемам, таким как:
(new Bar()) instanceof Bar; // false!!!! ???
и
var b = new Bar();
b.bar() // Uncaught TypeError: b.bar is not a function
Я застрял.
Есть ли способ встретиться? все следующие критерии:
- Мой publi c API должен неявно предоставлять "приватный" конструктор arg (но, по причинам ™ , я не могу сделать это в сам конструктор Foo ... это должно быть сделано через какую-то оболочку или перехват)
- Версия Foo API publi c должна быть тонким псевдонимом Foo. Таким образом, учитывая
f = new Foo()
, тогда f instanceof PublicFoo
должно вернуть true
- PublicFoo все еще должен поддерживать
extends
. Итак, учитывая class Bar extends PublicFoo
, тогда (new Bar()) instanceof Bar
должен вернуть true
.
Вот интерактивная демонстрация моей головоломки Proxy:
class Foo {
constructor(p, c) {
this.p = p;
this.c = c;
}
foo() {
return "foo";
}
}
var PublicFoo = new Proxy(Foo, {
construct(target, args) {
var p = "private";
return new target(p, ...args);
}
});
var foo = new Foo("private", "public");
console.assert( foo instanceof PublicFoo, "Foo instances are also instances of PublicFoo" );
console.assert( foo.p === "private" );
console.assert( foo.c === "public" );
var publicFoo = new PublicFoo("public");
console.assert( publicFoo instanceof Foo, "PublicFoo instances are also instances of Foo" );
console.assert( publicFoo.p === "private" );
console.assert( publicFoo.c === "public" );
class Bar extends PublicFoo {
constructor(c) {
super(c);
}
bar() {
return "bar";
}
}
var i = new Bar("public");
console.assert( i instanceof Bar, "new Bar() should return an instance of Bar" );
console.assert( i.p === "private" );
console.assert( i.c === "public" );
i.foo(); // "foo"
i.bar(); // Uncaught TypeError: i.bar is not a function