看看用TypeScript怎樣實現常見的設計模式,順便復習一下。
學模式最重要的不是記UML,而是知道什么模式可以解決什么樣的問題,在做項目時碰到問題可以想到用哪個模式可以解決,UML忘了可以查,思想記住就好。
這里盡量用原創的,實際中能碰到的例子來說明模式的特點和用處。
簡單工廠模式 Simple Factory
特點:把同類型產品對象的創建集中到一起,通過工廠來創建,添加新產品時只需加到工廠里即可,也就是把變化封裝起來,同時還可以隱藏產品細節。
用處:要new多個同一類型對象時可以考慮使用簡單工廠。
注意:對象需要繼承自同一個接口。
下面用TypeScript寫一個槍工廠來看看簡單工廠模式:
enum GunType{
AK,
M4A1,
}
interface Shootable{
shoot();
}
abstract class Gun implements Shootable{ // 抽象產品 - 槍
abstract shoot();
}
class AK47 extends Gun{ //具體產品 - AK47
shoot(){
console.log('ak47 shoot.');
}
}
class M4A1 extends Gun{ //具體產品 - M4A1
shoot(){
console.log('m4a1 shoot.');
}
}
class GunFactory{
static createGun(type: GunType): Gun{
switch(type){
case GunType.AK:
return new AK47();
case GunType.M4A1:
return new M4A1();
default:
throw Error('not support this gun yet');
}
}
}
GunFactory.createGun(GunType.AK).shoot();
GunFactory.createGun(GunType.M4A1).shoot();
//輸出
ak47 shoot.
m4a1 shoot.
上面代碼GunFactory
工廠就是根據類型來創建不同的產品,使用的時候只需要引入這個工廠和接口即可。
這樣就把變化封裝到了工廠中,如果以后要支持狙擊槍,只需要加個實現Gun
接口的Sniper
類就可以了。
工廠方法模式 Factory Method
特點:把工廠抽象出來,讓子工廠來決定怎么生產產品, 每個產品都由自己的工廠生產。
用處:當產品對象需要進行不同的加工時可以考慮工廠方法。
注意:這不是所謂的簡單工廠的升級版,兩者有不同的應用場景。
繼續用TypeScript寫一個槍工廠來看看工廠方法模式:
interface Shootable{
shoot();
}
abstract class Gun implements Shootable{ // 抽象產品 - 槍
abstract shoot();
}
class AK47 extends Gun{ //具體產品 - AK47
shoot(){
console.log('ak47 shoot.');
}
}
class M4A1 extends Gun{ //具體產品 - M4A1
shoot(){
console.log('m4a1 shoot.');
}
}
abstract class GunFactory{ //抽象槍工廠
abstract create(): Gun;
}
class AK47Factory extends GunFactory{ //Ak47工廠
create(): Gun{
let gun = new AK47(); // 生產Ak47
console.log('produce ak47 gun.');
this.clean(gun); // 清理工作
this.applyTungOil(gun);// Ak47是木頭槍托,塗上桐油
return gun;
}
private clean(gun: Gun){
//清洗
console.log('clean gun.');
}
private applyTungOil(gun: Gun){
//塗上桐油
console.log('apply tung oil.');
}
}
class M4A1Factory extends GunFactory{ //M4A1工廠
create(): Gun{
let gun = new M4A1(); // 生產M4A1
console.log('produce m4a1 gun.');
this.clean(gun); // 清理工作
this.sprayPaint(gun); // M4是全金屬,噴上漆
return gun;
}
private clean(gun: Gun){
//清洗
console.log('clean gun.');
}
private sprayPaint(gun: Gun){
//噴漆
console.log('spray paint.');
}
}
let ak47 = new AK47Factory().create();
ak47.shoot();
let m4a1 = new M4A1Factory().create();
m4a1.shoot();
//output
produce ak47 gun.
clean gun.
apply tung oil.
ak47 shoot.
produce m4a1 gun.
clean gun.
spray paint.
m4a1 shoot.
可以看到Ak47和M4A1在生產出來后的處理不一樣,Ak需要塗桐油,M4需要噴漆,用簡單工廠就比較難做到,所以就每個產品都弄個工廠來封裝各自己的生產過程。
另外的好處是當加入其他槍比如沙漠之鷹時,再加一個產品和產品工廠就好了,並不需要改變現有代碼,算是做到了遵守開閉原則。
缺點也明顯,增加一個產品就需要多加兩個類,增加了代碼復雜性。
抽象工廠模式 Abstract Factory
特點:同樣隱藏了具體產品的生產,不過生產的是多種類產品。
用處:當需要生產的是一個產品族,並且產品之間或多或少有關聯時可以考慮抽象工廠方法。
注意:和工廠方法的區別,工廠方法是一個產品, 而抽象工廠是產品族,線和面的區別。
繼續用槍,外加子彈,用TypeScript寫一個抽象槍工廠來看看抽象工廠模式:
interface Shootable{
shoot();
}
abstract class Gun implements Shootable{ // 抽象產品 - 槍
private _bullet: Bullet;
addBullet(bullet: Bullet){
this._bullet = bullet;
}
abstract shoot();
}
class AK47 extends Gun{ //具體產品 - AK47
shoot(){
console.log(`ak47 shoot with ${this._bullet}.`);
}
}
class M4A1 extends Gun{ //具體產品 - M4A1
shoot(){
console.log(`m4a1 shoot with ${this._bullet}.`);
}
}
abstract class Bullet{ // 抽象子彈
abstract name: string;
}
class AkBullet{ // AK 子彈
name: string = 'ak bullet';
}
class M4Bullet{ // m4a1 子彈
name: string = 'm4a1 bullet';
}
abstract class ArmFactory{ //抽象軍工廠
abstract createGun(): Gun;
abstract createBullet(): Bullet;
}
class AK47Factory extends ArmFactory{
createGun(): Gun{
let gun = new AK47(); // 生產Ak47
console.log('produce ak47 gun.');
this.clean(gun); // 清理工作
this.applyTungOil(gun);// Ak47是木頭槍托,塗上桐油
return gun;
}
private clean(gun: Gun){
//清洗
console.log('clean gun.');
}
private applyTungOil(gun: Gun){
//塗上桐油
console.log('apply tung oil.');
}
createBullet(): Bullet{
return new AkBullet();
}
}
class M4A1Factory extends ArmFactory{ //M4A1工廠
createGun(): Gun{
let gun = new M4A1(); // 生產M4A1
console.log('produce m4a1 gun.');
this.clean(gun); // 清理工作
this.sprayPaint(gun); // M4是全金屬,噴上漆
return gun;
}
private clean(gun: Gun){
//清洗
console.log('clean gun.');
}
private sprayPaint(gun: Gun){
//噴漆
console.log('spray paint.');
}
createBullet(): Bullet{
return new M4Bullet();
}
}
//使用
function shoot(gun: Gun, bullet: Bullet) // 使用生產的槍和子彈
{
gun.addBullet(bullet);
gun.shoot();
}
let akFactory = new AK47Factory();
shoot(akFactory.createGun(), akFactory.createBullet());
let m4a1Factory = new M4A1Factory();
shoot(m4a1Factory.createGun(), m4a1Factory.createBullet());
//輸出
produce ak47 gun.
clean gun.
apply tung oil.
add bullet: ak bullet
ak47 shoot with ak bullet.
produce m4a1 gun.
clean gun.
spray paint.
add bullet: m4a1 bullet
m4a1 shoot with m4a1 bullet.
工廠除了生產槍外還生產子彈,子彈和槍算是一個產品族,使用者接觸到的只有抽象工廠和抽象產品,隱藏了具體實現細節。
在大的框架下面有很多小項目時用抽象工廠配合如動態對象生成之類的技術就可以很容易實現靈活的架構。