假设我用面向对象语言(例如Python)实现了以下设计。我知道 OO 方式有点不自然,有时在使用 TypeScript 时不是首选。那么在 TypeScript 中实现相同设计的功能(和惯用)方法是什么?
想法是:
Car
类型来描述通用汽车。Car
类型,并且Car
类型。这将是 Python 代码:
from abc import ABC, abstractmethod
import json
class Car(ABC):
@abstractmethod
def start(self):
raise NotImplemented
class ManualGearboxCar(Car):
def start(self):
self.engage_clutch()
self.start_engine()
dashboard_info = self.read_dashboard()
print("Dashboard:", json.dumps(dashboard_info))
# ... Check dashboard_info and raise if anything goes wrong ...
if not dashboard_info["ok"]:
raise Exception("Something is not OK")
self.shift_gear(1)
self.release_clutch()
print("Car started!")
def read_dashboard(self) -> dict:
return {
"foo": "bar",
"default": "values",
"ok": True,
}
def engage_clutch(self):
print("[DefaultClutch] engaged!")
def release_clutch(self):
print("[DefaultClutch] released!")
@abstractmethod
def start_engine(self):
raise NotImplemented
@abstractmethod
def shift_gear(self, level: int):
raise NotImplemented
class Picasso(ManualGearboxCar):
def read_dashboard(self) -> dict:
info = super().read_dashboard()
return info | {
"brand": "Citroen",
}
def start_engine(self):
print("[Picasso] engine started!")
def shift_gear(self, level: int):
print("[Picasso] gear shifted to:", level)
class Tesla(Car):
def start(self):
print("[Tesla] Wow it just starts!")
if __name__ == "__main__":
picasso = Picasso()
picasso.start()
tesla = Tesla()
tesla.start()
这就是我在 TypeScript 中实现相同功能的想法,仍然采用 OO 风格,基本上是将相同的代码从 Python 移植到 TS:
type Car = {
start(): void;
}
abstract class ManualGearCar implements Car {
start(): void {
this.engageClutch();
this.startEngine();
const dashboardInfo = this.readDashboard();
// ... Check dashboard_info and raise if anything goes wrong ...
console.log(`Dashboard: ${JSON.stringify(dashboardInfo)}`);
if (!dashboardInfo.ok) {
throw new Error("Something is not OK");
}
this.shiftGear(1);
this.releaseClutch();
console.log("Car started!");
}
readDashboard(): Record<string, any> & { ok: boolean } {
return {
foo: "bar",
default: "values",
ok: true,
};
}
engageClutch(): void {
console.log("[DefaultClutch] engaged!");
}
releaseClutch(): void {
console.log("[DefaultClutch] released!");
}
abstract startEngine(): void;
abstract shiftGear(level: number): void;
}
class Picasso extends ManualGearCar {
readDashboard(): Record<string, any> & { ok: boolean; } {
const info = super.readDashboard();
return { ...info, brand: "Citroen" };
}
startEngine(): void {
console.log("[Picasso] engine started!");
}
shiftGear(level: number): void {
console.log(`[Picasso] gear shifted to: ${level}`);
}
}
const createPicasso = (): Car => {
return new Picasso();
}
const createTesla = (): Car => {
return {
start() {
console.log("[Tesla] Wow it just starts!");
}
}
}
function main() {
const picasso = createPicasso();
picasso.start();
const tesla = createTesla();
tesla.start()
}
main();
如果采用函数式风格,你会做什么?
谢谢!
看起来您正在描述模板方法模式。
如果您喜欢更实用的风格,就像许多其他设计模式一样,这可以使用高阶函数轻松实现。然后,您可以将函数作为参数传递来代替抽象方法。
您的示例目前有些人为,因为该类没有状态,因此可以简单地用简单的函数替换。假设您需要状态,您可以将方法替换为接受状态作为参数并返回更新后的状态的函数。我在下面添加了一个示例。为了保留在 ManualGearCar 的“子类”中扩展状态的能力,我将 create 函数(替换构造函数)设为通用。
namespace ManualGearCar {
export interface Car {
wheels: number;
gears: number;
currentGear: number;
engineStarted: boolean;
}
function readDashboard(): Record<string, any> & { ok: boolean } {
return {
foo: "bar",
default: "values",
ok: true,
};
}
function engageClutch(): void {
console.log("[DefaultClutch] engaged!");
}
function releaseClutch(): void {
console.log("[DefaultClutch] released!");
}
export function create<T extends Car>(state: T, startEngine: (state: T) => T, shiftGear: (state: T, gear: number) => T) : T {
engageClutch();
const state2 = startEngine(state);
const dashboardInfo = readDashboard();
// ... Check dashboard_info and raise if anything goes wrong ...
console.log(`Dashboard: ${JSON.stringify(dashboardInfo)}`);
if (!dashboardInfo.ok) {
throw new Error("Something is not OK");
}
const state3 = shiftGear(state2, 1);
releaseClutch();
console.log("Car started!");
return state3;
}
}
function createPicasso() {
const state = {
wheels: 4,
gears: 5,
currentGear: 0,
engineStarted: false
};
return ManualGearCar.create(
state,
(s: ManualGearCar.Car) => ({...s, engineStarted: true}),
(s: ManualGearCar.Car, gear: number) => ({...s, gear: gear}));
}
interface RaceCar extends ManualGearCar.Car {
maxSpeed: number
}
function createFormulaOneCar() {
const state = {
wheels: 4,
gears: 5,
currentGear: 0,
engineStarted: false,
maxSpeed: 400
};
return ManualGearCar.create(
state,
(s: RaceCar) => ({...s, engineStarted: true}),
(s: RaceCar, gear: number) => ({...s, gear: gear, maxSpeed: s.maxSpeed + 10}));
}