工厂模式是平时开发过程中最常见的设计模式。工厂模式解决类的实例化问题,它属于创建型模式。工厂模式也经常会和其他设计模式组合使用。
试想你去麦当劳买一个汉堡。你只需要告诉收银员要一个xx汉堡。过一会就会有一个此类型的汉堡被制作出来。而你完全不需要知道这个汉堡是怎么被制作出来的。这个例子中你就是客户端代码,麦当劳就是工厂,负责生产汉堡。汉堡是接口,而具体的某一种汉堡,比如说香辣鸡腿堡,就是实现了汉堡接口的类。
我们继续通过另外一个例子,深入理解工厂模式。现在我们给某款音乐软件开发一个推荐功能。需求是能够根据用户选择的音乐风格,推荐不同风格的歌曲清单。那么你打算怎么实现呢?
1. 音乐推荐器1.0版本
如果之前没有学习过设计模式,很可能你的实现会是这样。编写 RecommendMusicService
类,里面有一个 Recommend方法。根据输入的风格不同,执行不同的推荐逻辑。代码如下:
public class RecommendMusicService {
public List<String> recommend(String style) {
List<String> recommendMusicList = new ArrayList<>();
if ("metal".equals(style)) {
recommendMusicList.add("Don't cry");
} else if ("country".equals(style)) {
recommendMusicList.add("Hotel california");
} else if ("grunge".equals(style)) {
recommendMusicList.add("About a girl");
}else {
recommendMusicList.add("My heart will go on");
}
return recommendMusicList;
}
}
是不是觉得 recommed 方法太长了? OK,我们重构下,把每种音乐风格的推荐逻辑封装到相应的方法中。这样推荐方法就可以复用了。
public class RecommendMusicService {
public List<String> recommend(String style) {
List<String> recommendMusicList = new ArrayList<>();
if ("metal".equals(style)) {
recommendMetal(recommendMusicList);
} else if ("country".equals(style)) {
recommendCountry(recommendMusicList);
} else if ("grunge".equals(style)) {
recommendGrunge(recommendMusicList);
}else {
recommendPop(recommendMusicList);
}
return recommendMusicList;
}
private void recommendPop(List<String> recommendMusicList) {
recommendMusicList.add("My heart will go on");
recommendMusicList.add("Beat it");
}
private void recommendGrunge(List<String> recommendMusicList) {
recommendMusicList.add("About a girl");
recommendMusicList.add("Smells like teen spirit");
}
private void recommendCountry(List<String> recommendMusicList) {
recommendMusicList.add("Hotel california");
recommendMusicList.add("Take Me Home Country Roads");
}
private void recommendMetal(List<String> recommendMusicList) {
recommendMusicList.add("Don't cry");
recommendMusicList.add("Fade to black");
}
}
这样是不是很完美了!recommend 方法精简了很多,而且每种不同的推荐逻辑都被封装到相应的方法中了。那么,如果再加一种风格推荐怎么办?这有什么难,recommed 方法中加分支就好啦。然后在 RecommendMusicService
中增加一个对应的推荐方法。
等等,是不是哪里不太对?回想一下设计模式6大原则的开闭原则----对扩展开放,对修改关闭。面对新风格推荐的需求,我们一直都在修改 RecommendMusicService
这个类。以后每次有新风格推荐要添加,都会导致修改 RecommendMusicService
。显然这是个坏味道。
那么如何做到实现新的风格推荐需求时,满足开闭原则呢?
2. 音乐推荐器2.0版本
添加新需求时,如何做到不修改,去扩展?是不是想到了单一职责?是的,类的职责越单一,那么它就越稳定。RecommendMusicService
类的职责太多了,负责n种风格的推荐。OK,那么我们第一件事就是要减少 RecommendMusicService
类的职责,把每种不同风格的推荐提取到不同的类当中。
比如MetalMusicRecommendService
、PopMusicRecommendService
、CountryMusicRecommendService
。这些类都可以通过 recommed 方法生成推荐的歌曲清单。而 RecommendMusicService
类只是通过调用不同 MusicRecommendService
的 recommed 方法来实现推荐。代码如下:
MetalMusicRecommendService 类:
public class MetalMusicRecommendService {
public List<String> recommend(){
List<String> recommendMusicList = new ArrayList<>();
recommendMusicList.add("Don't cry");
recommendMusicList.add("Fade to black");
return recommendMusicList;
}
}
同类型的还有 GrungeMusicRecommendService
、PopMusicRecommendService
、CountryMusicRecommendService
类
现在我们来改造 MusicRecommendService
类:
public class RecommendMusicService {
private MetalMusicRecommendService metalMusicRecommendService = new MetalMusicRecommendService();
private GrungeMusicRecommendService grungeMusicRecommendService = new GrungeMusicRecommendService();
private CountryMusicRecommendService countryMusicRecommendService = new CountryMusicRecommendService();
private PopMusicRecommendService popMusicRecommendService = new PopMusicRecommendService();
public List<String> recommend(String style) {
List<String> recommendMusicList = new ArrayList<>();
if ("metal".equals(style)) {
metalMusicRecommendService.recommend();
} else if ("country".equals(style)) {
countryMusicRecommendService.recommend();
} else if ("grunge".equals(style)) {
grungeMusicRecommendService.recommend();
}else {
popMusicRecommendService.recommend();
}
return recommendMusicList;
}
}
改造后,如果有了新音乐风格推荐的需求,只需要增加相应的 xxxMusicRecommendService
类。然后在 RecommendMusicService
中增加相应分支即可。这样就做到了开闭原则。那么还有什么违背设计原则的地方吗?RecommendMusicService
是不是依赖的 xxMusicRecommendService
类太多了?
没错,而且这么多类,实际上都是做推荐的事情,且都是通过 recommend 方法提供推荐结果。这完全可以抽象出接口,比如 MusicRecommendInterface
。那么 RecommendMusicService
依赖 MusicRecommendInterface
就可以了。这解决了依赖反转问题----应该依赖接口,而不是依赖具体实现。需要更多资料的小伙伴可以加入神秘组织(微):1253431195
获取更多 程序员架构学习资料,
技术也是在不断更新,我相信你也想学到最新技术,
所以你们就不要说我骗人的,加的都获取资料,
学习更上一层楼了!Java,人工智能,架构师,初级程序员提升都有!
我们又复习了单一职责和依赖反转原则。不愧是指导设计模式的原则,真的是无处不在。依赖 MusicRecommendInterface
没问题,但是不同的音乐风格,怎么能实例化 MusicRecommendInterface
的某个具体实现呢?工厂模式于是就应运而生了!
3. 音乐推荐器3.0版本
我们回顾一下文章开头说到,工厂模式解决的是类的实例化。无论你需要哪种风格的 MusicRecommendService
,只需要告诉工厂,工厂会给你实例化好你需要的具体实现。而工厂能做到这些是基于继承和多态。
RecommendMusicService
只需要依赖 MusicRecommendInterface
,具体需要哪个MusicRecommendService
的实现,只需要告诉 RecommendServiceFactory
即可。MusicRecommendService
拿到具体的实现后调用它的 recommand 方法,就可以得到相应风格的推荐歌曲清单。
首先我们需要定义所有 MusicRecommendService
要实现的接口,很简单,只有一个 recommend 方法:
public interface MusicRecommendInterface {
List<String> recommend();
}
我们2.0版本中的 xxxMusicRecommendService
都需要实现此接口,例如:
public class GrungeMusicRecommendService implements MusicRecommendInterface {
public List<String> recommend() {
List<String> recommendMusicList = new ArrayList<>();
recommendMusicList.add("About a girl");
recommendMusicList.add("Smells like teen spirit");
return recommendMusicList;
}
}
不同音乐风格的推荐逻辑在各自实现的 recommend() 方法中。
下面就是工厂模式中的工厂代码了,其实很简单,只是根据不同的参数实例化不同的实现并返回。
public class MusicRecommendServiceFactory {
MusicRecommendInterface createMusicRecommend(String style) {
if ("metal".equals(style)) {
return new MetalMusicRecommendService();
} else if ("country".equals(style)) {
return new CountryMusicRecommendService();
} else if ("grunge".equals(style)) {
return new GrungeMusicRecommendService();
} else {
return new PopMusicRecommendService();
}
}
}
我们再来看看 RecommendMusicService
的代码:
public class RecommendMusicService {
private MusicRecommendServiceFactory recommendMusicServiceFactory = new MusicRecommendServiceFactory();
public List<String> recommend(String style) {
MusicRecommendInterface musicRecommend = recommendMusicServiceFactory.createMusicRecommend(style);
return musicRecommend.recommend();
}
}
是不是简单多了,已经不再依赖那么多的 MusicRecommendInterface
的实现了。它要做的事情仅仅是通过工厂得到想要的 RecommendMusicService
实现,然后调用它的 recommend() 方法,就可以得到你想要的推荐结果。需要更多资料的小伙伴可以加入神秘组织(微):1253431195
获取更多 程序员架构学习资料,
技术也是在不断更新,我相信你也想学到最新技术,
所以你们就不要说我骗人的,加的都获取资料,
学习更上一层楼了!Java,人工智能,架构师,初级程序员提升都有!
类图如下:
以上三种实现方式总结如下:
4. 小结
本节我们通过音乐推荐器的例子,实践了如何找到程序中违反设计原则的地方,并通过工厂模式来解决这些问题。使用设计模式可以让程序更符合程序设计原则,从而写出更为健壮的代码。我们应牢记工厂模式解决的是类的实例化问题。这个例子很简单,不过涉及到的知识点却很多。有封装、多态、单一职责和依赖反转等。可见要想把程序设计好,必须熟练掌握这些基本概念和原则。