设计模式原则
单一功能原则(Single Responsibility Principle)
开放封闭原则(Opened Closed Principle)
里式替换原则(Liskov Substitution Principle)
接口隔离原则(Interface Segregation Principle)
依赖反转原则(Dependency Inversion Principle)
单例模式 单例模式 使用单例模式保证了只存在一个实例,每次获取返回的都是这个实例 ,当我们对一个类通过 new 关键字生成多个对象,这些对象其实都是同一个,也就是第一次 new 创建的实例。
方式一 1 2 3 4 5 6 7 8 9 10 11 12 class Singleton { static getInstance = function ( ) { if (!Singleton .instance ) { Singleton .instance = new Singleton (); } return Singleton .instance ; } }const s1 = Singleton .getInstance ();const s2 = Singleton .getInstance ();console .log (s1 === s2)
这里的 s1 和 s2 都是通过 getInstance 方法创建的,getInstance 方法其实一直返回的都是 Singleton.instance 当初次调用 getInstance 方法时会创建一个 Singleton 实例赋值给 Singleton.instance 以后每次调用该方法返回的都是这个对象。
方式二(闭包) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Singleton { }Singleton .getInstance = (function ( ) { let instance = null ; return function ( ) { if (!instance) { instance = new Singleton (); } return instance; } })();const s1 = Singleton .getInstance ();const s2 = Singleton .getInstance ();console .log (s1 === s2)
上面两种方法有一个共同的缺点,就是我们获取实例还需要去调用 getInstance 方法,这其实带来很多不便,而且也不够直观透明,下面我们通过使用代理类的方式来创建一个透明的单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Singleton { }const ProxySingleton = (function ( ) { let instance = null ; return function ( ) { if (!instance){ instance = new Singleton (); } return instance; } })()const s1 = new ProxySingleton ();const s2 = new ProxySingleton ();console .log (s1 === s2)
使用代理类后,我们直接获取代理类的实例,而代理类返回的就是我们 Singleton 的实例,这里就是通过创建一个构造方法的实例时,如果该方法返回的是一个 object 则实例就是这个返回值 的小tips来实现我们的这个功能。
可以看到无论是哪种方法,最重要的一点就是要有一个值来存实例,每次返回的都是这个实例,这样才能保证每次都是相同的值。
工厂模式 工厂模式就是把我们重复的操作交给工厂 来帮我们完成,使用工厂函数就需要我们去考虑哪些东西是不变的,又有哪些是变化的,不变的东西是不是就可以抽离出来单独封装。
例如我们现在有一个做蛋糕的工厂,需要对每款蛋糕信息进行统计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function CakeA (name, price) { this .name = name; this .price = price; this .material = ['黄油' , '巧克力' , '面粉' ] }function CakeB (name, price) { this .name = name; this .price = price; this .material = ['黄油' , '芒果' , '奶油' , '面粉' ] }function CakeFactory (name, price, type) { switch (type) { case "A" : return new CakeA (name, price); case "B" : return new CakeB (name, price); default : } }
以上代码我们创建了两款蛋糕,再通过 CakeFactory 工厂来根据不同的类型创建不同的类型蛋糕。不过以上代码存在一个致命的问题,目前只有两款蛋糕还好说,如果有几十种岂不是要创建几十个蛋糕构造函数吗,对于不同类型的蛋糕来说,他们的”变”不就是材料的区别么,那我们把他们的名称,价格单独拿出来,只是根据类型去判断它的原材料不就可以了么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function Cake (name, price, material ){ this .name = name; this .price = price; this .material = material; }function CakeFactory (name, price, type) { let material; switch (type) { case "A" : material = ['黄油' , '巧克力' , '面粉' ]; break ; case "B" : material = ['黄油' , '芒果' , '奶油' , '面粉' ]; break ; default : } }
抽象工厂模式 首先理解抽象的概念,在 Java 中可以通过 abstract 来定义一个抽象类,继承该类就必须要实现它内部的抽象方法。那我们在 Javascript 中如何模拟呢?
还是一个蛋糕工厂,我们制作蛋糕就需要面粉和黄油,而面粉和黄油又各自有自己的工厂,下面我们用代码来模拟实现。
1 2 3 4 5 6 7 8 9 class CakeFactory { createButter ( ) { throw new Error ("You need to make it happen yourself" ) } createFlour ( ) { throw new Error ("You need to make it happen yourself" ) } }
我们通过抛出错误来模拟抽象类 的效果,蛋糕工厂必须要有黄油和面粉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class FlourFactory { getType ( ) { throw new Error ("You need to make it happen yourself" ) } }class TypeAFlourFactory extends FlourFactory { getType ( ) { return "A类型面粉" } }class ButterFactory { getType ( ) { throw new Error ("You need to make it happen yourself" ) } }class TypeAButterFactory extends ButterFactory { getType ( ) { return "A类型黄油" } }
然后面粉和黄油又各自有工厂并实现
1 2 3 4 5 6 7 8 class MyCakeFactory extends CakeFactory { createButter ( ) { return new TypeAButterFactory (); } createFlour ( ) { return new TypeAFlourFactory (); } }
我们制作蛋糕就使用到了面粉和黄油工厂的产物
1 2 3 4 const cake = new MyCakeFactory ();const cakeFlour = cake.createFlour ();const cakeButter = cake.createButter ();console .log (`这个蛋糕由${cakeFlour.getType()} 和${cakeButter.getType()} 做成的` )
最后完成制作蛋糕的过程。