案例需求: 一个人想买一个电脑。
简单实现:
class User { getComputer () { console.log('跑很远去买220v电源'),//表达的是这个过程很复杂, console.log('跑很远买16存显示器') //。。。。。 console.log('组装'); }};new User().getComputer();
上面的实现满足了我的需求。但是有个问题:在getComputer方法中,每次都要跑很远去买,然后自己组装,整个过程繁琐复杂(脑补getComputer方法内部的代码很长很长)。如果能有人送货,就好了。
解决思路就是单一原则: 一个类或方法只做一件事。
class User { dianyuan(num) { console.log(`买了个${num}v电源`) } screen(num) { console.log(`买了个${num}寸显示器`) } zz() { console.log('组装') } getComputer () { this.dianyuan(220); this.screen(20); //。。。。。 this.zz(); }};new User().getComputer();
在新实现中,我随便打几个电话(将不同类型的零件单独定义为各自的函数,调用dianyuan/screen...,实现单一原则),就能得到想要类型的零件。但是,不是每个人都像我这样了解电脑零件及组装过程的,对于小白来说,它希望有个经销商能直接帮他完成零件购买及组装的过程,
解决思路:再次使用单一原则
class Computer { dianyuan() { console.log(`买了个200v电源`) } screen(num) { console.log(`买了个17寸显示器`) } all() { this.dianyuan(); this.screen(); }}class User { zz() { console.log('组装') } getComputer () { let computer = new Computer(220, 20); computer.all(); this.zz(); }};new User().getComputer();
这次实现中, 我将获取零件的过程封装成了一个类,它能返回组装一个电脑所需要的所有零件;从此小白获取一个电脑的方法就是调用以下Computer.all()方法。但是只有一个经销商实在是太被动了,我不能货比三家,我希望所有的经销商都具备获取所有零件的能力,但是各自零件的类型及价钱又能让我可以选择。
解决思路:
1抽象:抽取事物的本质,剔除具体表现。
2里氏替换:子类能够出现在所有父类出现的地方。
3依赖倒置:程序应该依赖抽象接口, 而不依赖具体实现。
class SuperComputer { dianyuan() { } screen() { } all() { this.dianyuan(); this.screen(); }}class SubOneComputer extends SuperComputer{ dianyuan() { console.log('我们家的220电源10元') } screen() { console.log('我们家17屏幕20元'); }}class SubTwoComputer extends SuperComputer { dianyuan() { console.log('我们家的220电源20元') } screen() { console.log('我们家17屏幕10元'); }}class User { zz() { console.log('组装') } getComputer (computer) { //为了程序的健壮性, 你需要判断这个参数是否是某经销商的实例(能够调用all()),从而产生某个经销商的依赖, 然后最好的实现方式就是判断它是否是某个抽象类的实例,避免对扩展子类的依赖, 这就是依赖倒置原则 if (computer instanceof SuperComputer) { computer.all(); this.zz(); } else { console.log('请传入经销商的实例') } }};let xiaoming= new User();xiaoming.getComputer(new SubOneComputer());let xiaohong = new User()xioahong.getComputer(new SubTwoComputer());
小红/小明通过不同经销商那拿到不同类型的零件,但是零件肯定是齐全的,这得力于两点。
1:抽象类SuperComputer为所有的经销商定义了标准(是不是有点抽象接口的概念)。
2:经销商实现了SuperComputer定义的所有零件方法。这就是里氏替换带来的好处。
3:为了User.getComputer()的健壮性, 你需要判断computer是否是某经销商的实例(能够调用all()),从而产生某个经销商的依赖, 然后最好的实现方式就是判断它是否是某个抽象类的实例,从而避免对扩展子类的依赖,毕竟抽象类的最可靠。
然后 而不然后经销商又了新的问题, 为了盈利,他必须扩大业务范围。
解决思路:
开闭原则:对修改关闭, 对扩展开放
class SuperComputer { dianyuan() { } screen() { } all() { this.dianyuan(); this.screen(); }}class SubOneComputer extends SuperComputer{ dianyuan() { console.log('我们家的220电源10元') } screen() { console.log('我们家17屏幕20元'); } paper() { console.log('A4纸1毛一张') }}class SubTwoComputer extends SuperComputer { dianyuan() { console.log('我们家的220电源20元') } screen() { console.log('我们家17屏幕10元'); } netline() { console.log('网线1米2元') }}class User { zz() { console.log('组装') } getComputer (computer) { //为了程序的健壮性, 你需要判断这个参数是否是某经销商的实例(能够调用all()),从而产生某个经销商的依赖, 然后最好的实现方式就是判断它是否是某个抽象类的实例,避免对扩展子类的依赖, 这就是依赖倒置原则 if (computer instanceof SuperComputer) { computer.all(); this.zz(); } else { console.log('请传入经销商的实例') } }};let xiaoming= new User();xiaoming.getComputer(new SubOneComputer());let xiaohong = new User()xioahong.getComputer(new SubTwoComputer());
上面的两个经销商为了利润, 各自扩大了经营范围(paper(), netline()),他们是对SupterComputer的扩展,而不能为单独的经销商在SuperComputer类中去添加跟电脑零件无关的方法,保证抽象的单一性,稳定性。
还有一个问题, 现在所有的小白能轻松的从不同的经销商那里获取全部零件,但是还是要自己组装, 还不够方便,其实小白们只需要找到一个会组装的人,这个组装的人肯定对于电脑零件是非常熟悉的,小白们不在关心电脑零件问题,。
解决思路:
迪米特法则:一个对象应该对其他对象保持最少的了解
class SuperComputer { dianyuan() { } screen() { } all() { this.dianyuan(); this.screen(); }}class SubOneComputer extends SuperComputer{ dianyuan() { console.log('我们家的220电源10元') } screen() { console.log('我们家17屏幕20元'); } paper() { console.log('A4纸1毛一张') }}class SubTwoComputer extends SuperComputer { dianyuan() { console.log('我们家的220电源20元') } screen() { console.log('我们家17屏幕10元'); } netline() { console.log('网线1米2元') }}//组装类class InitComputer { software() { console.log('安装软件') } hardware(computer) { if (computer instanceof SuperComputer) { computer.all(); } else { console.log('请传入经销商的实例') } } zz(money) { switch(money) { case 5000: this.hardware(new SubOneComputer());//5000元的配置 case 4000: this.hardware(new SubTwoComputer());//4000元的配置 } this.software(); console.log('组装完毕'); }}class User { getComputer (money) { new InitComputer().zz(money); }};let xiaoming= new User();xiaoming.getComputer(4000);let xiaohong = new User()xioahong.getComputer(5000);
终于搞定了,现在小白只需要给组装类money就能得到一台相应配置的电脑, 不用在找经销商及自己组装了。这就是迪米特法则, 小白不需要熟悉经销商的套路,而是通过组装类这个中间人去搞定。
以上的例子是我对公司培训面向对象6大基本原则的理解,总结以下:单一原则,开闭原则,里氏替换,抽象接口,依赖倒置,迪米特法则