[ini]
This commit is contained in:
commit
51431ec4cf
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/temp/
|
||||
/build/
|
6
source/base.ts
Normal file
6
source/base.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
declare var process;
|
||||
|
||||
function show(message : string): void {
|
||||
process.stdout.write("## " + message + "\n");
|
||||
}
|
||||
|
57
source/composition.ts
Normal file
57
source/composition.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* creates an object, which has all the methods of a class instance, but as
|
||||
* plain functions (required for composition)
|
||||
*/
|
||||
function dullCopy<X>(instance: X): X {
|
||||
const methodBlacklist: Set<string> = new Set<string>([
|
||||
"__defineGetter__",
|
||||
"__defineSetter__",
|
||||
"__lookupGetter__",
|
||||
"__lookupSetter__",
|
||||
"__proto__",
|
||||
"constructor",
|
||||
"isPrototypeOf",
|
||||
"hasOwnProperty",
|
||||
"propertyIsEnumerable",
|
||||
"valueOf",
|
||||
"toString",
|
||||
"toLocaleString",
|
||||
]);
|
||||
const result = {};
|
||||
// attributes
|
||||
{
|
||||
for (const [name, value] of Object.entries(instance)) {
|
||||
result[name] = value;
|
||||
}
|
||||
}
|
||||
// methods
|
||||
{
|
||||
const methodNames: Array<string> = Object.getOwnPropertyNames(instance["__proto__"]);
|
||||
for (const methodName of methodNames) {
|
||||
if (! methodBlacklist.has(methodName)) {
|
||||
result[methodName] = function (...methodArgs) {
|
||||
return instance[methodName].apply(instance, methodArgs);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return (result as X);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a combination of two class instances, forming a new object with the
|
||||
* methods of both operands (as plain functions)
|
||||
*/
|
||||
function compose<X, Y>(x: X, y: Y): (X & Y) {
|
||||
const result = {};
|
||||
for (const object of [x, y]) {
|
||||
for (const [name, implementation] of Object.entries(dullCopy(object))) {
|
||||
if (name in result) {
|
||||
process.stderr.write("[warn] overwriting method '" + name + "'\n");
|
||||
}
|
||||
result[name] = implementation;
|
||||
}
|
||||
}
|
||||
return (result as (X & Y));
|
||||
}
|
||||
|
49
source/logic/bookkeeper.ts
Normal file
49
source/logic/bookkeeper.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
class Bookkeeper implements Employee {
|
||||
private identity: Identity;
|
||||
private occupation: Occupation;
|
||||
|
||||
public constructor(name: string) {
|
||||
this.identity = new Identity("bookkeeper", name);
|
||||
this.occupation = new Calculating();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public introduce(): void {
|
||||
this.identity.introduce();
|
||||
}
|
||||
public work(): void {
|
||||
this.occupation.work();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function makeBookkeeper(name: string): Employee {
|
||||
return compose(
|
||||
new Identity("smartly composed bookkeeper", name),
|
||||
new Calculating()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
9
source/logic/calculating.ts
Normal file
9
source/logic/calculating.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
class Calculating implements Occupation {
|
||||
public constructor() {
|
||||
}
|
||||
|
||||
public work(): void {
|
||||
show("*type* *type* *type*");
|
||||
}
|
||||
}
|
||||
|
9
source/logic/cleaning.ts
Normal file
9
source/logic/cleaning.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
class Cleaning implements Occupation {
|
||||
public constructor() {
|
||||
}
|
||||
|
||||
public work(): void {
|
||||
show("*sweep* *sweep* *sweep*");
|
||||
}
|
||||
}
|
||||
|
5
source/logic/employee.ts
Normal file
5
source/logic/employee.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
interface Employee {
|
||||
introduce(): void;
|
||||
work(): void;
|
||||
}
|
||||
|
14
source/logic/identity.ts
Normal file
14
source/logic/identity.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
class Identity {
|
||||
private title: string;
|
||||
private name: string;
|
||||
|
||||
public constructor(title: string, name: string) {
|
||||
this.title = title;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public introduce(): void {
|
||||
show(`Hi! My name is ${this.name} and i'm the ${this.title} here.`);
|
||||
}
|
||||
}
|
||||
|
49
source/logic/janitor.ts
Normal file
49
source/logic/janitor.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
class Janitor implements Employee {
|
||||
private identity: Identity;
|
||||
private occupations: Array<Occupation>;
|
||||
|
||||
public constructor(name: string) {
|
||||
this.identity = new Identity("janitor", name);
|
||||
this.occupations = [
|
||||
new Repairing(),
|
||||
new Cleaning(),
|
||||
];
|
||||
}
|
||||
|
||||
public introduce(): void {
|
||||
this.identity.introduce();
|
||||
}
|
||||
public work(): void {
|
||||
for (const occupation of this.occupations) {
|
||||
occupation.work();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function makeJanitor(name: string): Employee {
|
||||
return compose(
|
||||
compose(
|
||||
new Identity("smartly composed janitor", name),
|
||||
new Repairing()
|
||||
),
|
||||
new Cleaning()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
4
source/logic/occupation.ts
Normal file
4
source/logic/occupation.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
interface Occupation {
|
||||
work(): void;
|
||||
}
|
||||
|
9
source/logic/repairing.ts
Normal file
9
source/logic/repairing.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
class Repairing implements Occupation {
|
||||
public constructor() {
|
||||
}
|
||||
|
||||
public work(): void {
|
||||
show("*fix* *fix* *fix*");
|
||||
}
|
||||
}
|
||||
|
10
source/main.ts
Normal file
10
source/main.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
const employees: Array<Employee> = [
|
||||
new Bookkeeper("Alice"),
|
||||
new Janitor("Bob"),
|
||||
];
|
||||
|
||||
for (const employee of employees) {
|
||||
employee.introduce();
|
||||
employee.work();
|
||||
}
|
||||
|
4
tools/build
Executable file
4
tools/build
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
make --file=tools/makefile
|
||||
|
41
tools/makefile
Normal file
41
tools/makefile
Normal file
|
@ -0,0 +1,41 @@
|
|||
## consts
|
||||
|
||||
dir_source := source
|
||||
dir_temp := temp
|
||||
dir_build := build
|
||||
cmd_mkdir := mkdir --parents
|
||||
cmd_tsc := tsc --lib es2020,dom
|
||||
cmd_log := echo "--"
|
||||
cmd_chmod := chmod
|
||||
cmd_cat := cat
|
||||
cmd_echo := echo
|
||||
|
||||
|
||||
## rules
|
||||
|
||||
.PHONY: _default
|
||||
_default: ${dir_build}/mixin
|
||||
|
||||
${dir_temp}/mixin.js: \
|
||||
${dir_source}/base.ts \
|
||||
${dir_source}/composition.ts \
|
||||
${dir_source}/logic/identity.ts \
|
||||
${dir_source}/logic/occupation.ts \
|
||||
${dir_source}/logic/calculating.ts \
|
||||
${dir_source}/logic/repairing.ts \
|
||||
${dir_source}/logic/cleaning.ts \
|
||||
${dir_source}/logic/employee.ts \
|
||||
${dir_source}/logic/bookkeeper.ts \
|
||||
${dir_source}/logic/janitor.ts \
|
||||
${dir_source}/main.ts
|
||||
@ ${cmd_log} "compiling …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_tsc} --outFile $@ $^
|
||||
|
||||
${dir_build}/mixin: ${dir_temp}/mixin.js
|
||||
@ ${cmd_log} "finishing …"
|
||||
@ ${cmd_mkdir} $(dir $@)
|
||||
@ ${cmd_echo} "#!/usr/bin/env node\n" > $@
|
||||
@ ${cmd_cat} $^ >> $@
|
||||
@ ${cmd_chmod} +x $@
|
||||
|
Loading…
Reference in a new issue