Skip to content
This repository has been archived by the owner on Jul 16, 2023. It is now read-only.

Commit

Permalink
chore: support extension through abstract base class (#18)
Browse files Browse the repository at this point in the history
This is a proposal based on [this comment](https://github.com/monadahq/polycons/pull/15/files#r933560022). 

The basic idea is that both the polycon class (e.g. `Dog`) and the concrete class (`Poodle`) extend a common abstract base type (e.g. `DogBase`).

This has the following effects:

1. No need to protect against stack overflow (the concrete class doesn't actually extend the polycon).
2. It is now possible to instantiate any concrete type directly (added a test that instantiates a `Labrador` even if when `Dog` resolves to `Poodle`.
3. We can still share common implementation at the base.
4. Enforce that concrete classes actually implement all abstract methods and properties (previously this was only by convention).

The polycon constructor has a quirky pattern:

```ts
constructor(scope, id, props) {
  super(null as any, id, props);
  return Polycons.create(QUALIFIER, scope, id, props);
}
```

The call to `super()` with `null` as the `scope` does two things:
1. The base construct is not attached to our tree.
2. The `null` can be used by the base class to bail out early:

```ts
class DogBase {
  public readonly foo: number;

  constructor(scope, id, props) {
    super(scope, id);
  
    if (!scope) {
      // initialize all readonly props with dummy values, this obj is thrown away
      this.foo = -1;
      return;
    }

    this.foo = 7947; // <-- the real thing
  }
}
```
  • Loading branch information
eladb authored Jul 31, 2022
1 parent 87596f3 commit 769f934
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 169 deletions.
152 changes: 43 additions & 109 deletions API.md
Original file line number Diff line number Diff line change
@@ -1,114 +1,5 @@
# API Reference <a name="API Reference" id="api-reference"></a>

## Constructs <a name="Constructs" id="Constructs"></a>

### Polycon <a name="Polycon" id="@monadahq/polycons.Polycon"></a>

A polymorphic construct that can be resolved at construction time into a more specific construct.

#### Initializers <a name="Initializers" id="@monadahq/polycons.Polycon.Initializer"></a>

```typescript
import { Polycon } from '@monadahq/polycons'

new Polycon(qualifier: string, scope: Construct, id: string, props?: any)
```

| **Name** | **Type** | **Description** |
| --- | --- | --- |
| <code><a href="#@monadahq/polycons.Polycon.Initializer.parameter.qualifier">qualifier</a></code> | <code>string</code> | *No description.* |
| <code><a href="#@monadahq/polycons.Polycon.Initializer.parameter.scope">scope</a></code> | <code>constructs.Construct</code> | *No description.* |
| <code><a href="#@monadahq/polycons.Polycon.Initializer.parameter.id">id</a></code> | <code>string</code> | *No description.* |
| <code><a href="#@monadahq/polycons.Polycon.Initializer.parameter.props">props</a></code> | <code>any</code> | *No description.* |

---

##### `qualifier`<sup>Required</sup> <a name="qualifier" id="@monadahq/polycons.Polycon.Initializer.parameter.qualifier"></a>

- *Type:* string

---

##### `scope`<sup>Required</sup> <a name="scope" id="@monadahq/polycons.Polycon.Initializer.parameter.scope"></a>

- *Type:* constructs.Construct

---

##### `id`<sup>Required</sup> <a name="id" id="@monadahq/polycons.Polycon.Initializer.parameter.id"></a>

- *Type:* string

---

##### `props`<sup>Optional</sup> <a name="props" id="@monadahq/polycons.Polycon.Initializer.parameter.props"></a>

- *Type:* any

---

#### Methods <a name="Methods" id="Methods"></a>

| **Name** | **Description** |
| --- | --- |
| <code><a href="#@monadahq/polycons.Polycon.toString">toString</a></code> | Returns a string representation of this construct. |

---

##### `toString` <a name="toString" id="@monadahq/polycons.Polycon.toString"></a>

```typescript
public toString(): string
```

Returns a string representation of this construct.

#### Static Functions <a name="Static Functions" id="Static Functions"></a>

| **Name** | **Description** |
| --- | --- |
| <code><a href="#@monadahq/polycons.Polycon.isConstruct">isConstruct</a></code> | Checks if `x` is a construct. |

---

##### ~~`isConstruct`~~ <a name="isConstruct" id="@monadahq/polycons.Polycon.isConstruct"></a>

```typescript
import { Polycon } from '@monadahq/polycons'

Polycon.isConstruct(x: any)
```

Checks if `x` is a construct.

###### `x`<sup>Required</sup> <a name="x" id="@monadahq/polycons.Polycon.isConstruct.parameter.x"></a>

- *Type:* any

Any object.

---

#### Properties <a name="Properties" id="Properties"></a>

| **Name** | **Type** | **Description** |
| --- | --- | --- |
| <code><a href="#@monadahq/polycons.Polycon.property.node">node</a></code> | <code>constructs.Node</code> | The tree node. |

---

##### `node`<sup>Required</sup> <a name="node" id="@monadahq/polycons.Polycon.property.node"></a>

```typescript
public readonly node: Node;
```

- *Type:* constructs.Node

The tree node.

---


## Structs <a name="Structs" id="Structs"></a>

Expand Down Expand Up @@ -445,11 +336,54 @@ public resolveConstruct(qualifier: string, scope: IConstruct, id: string, props?

| **Name** | **Description** |
| --- | --- |
| <code><a href="#@monadahq/polycons.PolyconFactory.newInstance">newInstance</a></code> | Creates a new instance of a polycons by resolving it through the registered factory. |
| <code><a href="#@monadahq/polycons.PolyconFactory.of">of</a></code> | Returns the polycon factory registered in a given scope. |
| <code><a href="#@monadahq/polycons.PolyconFactory.register">register</a></code> | Adds a factory at the root of the construct tree. |

---

##### `newInstance` <a name="newInstance" id="@monadahq/polycons.PolyconFactory.newInstance"></a>

```typescript
import { PolyconFactory } from '@monadahq/polycons'

PolyconFactory.newInstance(qualifier: string, scope: IConstruct, id: string, props?: any)
```

Creates a new instance of a polycons by resolving it through the registered factory.

###### `qualifier`<sup>Required</sup> <a name="qualifier" id="@monadahq/polycons.PolyconFactory.newInstance.parameter.qualifier"></a>

- *Type:* string

The type qualifier.

---

###### `scope`<sup>Required</sup> <a name="scope" id="@monadahq/polycons.PolyconFactory.newInstance.parameter.scope"></a>

- *Type:* constructs.IConstruct

The construct scope.

---

###### `id`<sup>Required</sup> <a name="id" id="@monadahq/polycons.PolyconFactory.newInstance.parameter.id"></a>

- *Type:* string

The construct identifier.

---

###### `props`<sup>Optional</sup> <a name="props" id="@monadahq/polycons.PolyconFactory.newInstance.parameter.props"></a>

- *Type:* any

The construct props.

---

##### `of` <a name="of" id="@monadahq/polycons.PolyconFactory.of"></a>

```typescript
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from "./process";
export * from "./polycon";
export * from "./polycon-factory";
20 changes: 20 additions & 0 deletions src/polycon-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,26 @@ export abstract class PolyconFactory {
});
}

/**
* Creates a new instance of a polycons by resolving it through the registered
* factory.
*
* @param qualifier The type qualifier
* @param scope The construct scope
* @param id The construct identifier
* @param props The construct props
* @returns The resolved construct
*/
public static newInstance(
qualifier: string,
scope: IConstruct,
id: string,
props?: any
) {
const factory = PolyconFactory.of(scope);
return factory.resolveConstruct(qualifier, scope, id, props);
}

public abstract resolveConstruct(
qualifier: string,
scope: IConstruct,
Expand Down
48 changes: 0 additions & 48 deletions src/polycon.ts

This file was deleted.

Loading

0 comments on commit 769f934

Please sign in to comment.