目录
简单工厂模式
由一个工厂对象决定创建出哪一种产品类的实例。工厂对象根据入参来决定返回哪一种对象。
类型:创建型,但不属于GOF23种设计模式。简单工厂更像是一种编码风格或是习惯。
使用场景:工厂类负责创建的对象比较少。客户端(应用层)只知道传入工厂类的参数,不关心创建对象的逻辑。
优点:只需要传入一个正确的参数,就可以获取你所需要的对象,无须知道其创建细节。
缺点:工厂类的职责相对过重,在增加新的产品类的时候,需要修改工厂类的判断逻辑,违背开闭原则。
例子:
package com.haozi.design.pattern.creational.simplefactory.dto;
public abstract class Game {
public abstract void buy();
}
package com.haozi.design.pattern.creational.simplefactory.dto;
public class PCGame extends Game{
@Override
public void buy() {
System.out.println("从stream上买游戏");
}
}
package com.haozi.design.pattern.creational.simplefactory.dto;
public class PS4Game extends Game {
@Override
public void buy() {
System.out.println("从ps store上买游戏");
}
}
package com.haozi.design.pattern.creational.simplefactory;
import com.haozi.design.pattern.creational.simplefactory.dto.Game;
import com.haozi.design.pattern.creational.simplefactory.dto.PCGame;
public class Test {
public static void main(String[] args) {
Game game = new PCGame();
game.buy();
}
}
上面这种方式,应用层直接创建对象,对于结构来说,这种做法比较不管是后期扩展还是结构都不好,用简单工厂模式改一下:
package com.haozi.design.pattern.creational.simplefactory;
public class GameFactory {
public Game getGame(String type){
if ("pc".equalsIgnoreCase(type)){
return new PCGame();
}else if("ps4".equalsIgnoreCase(type)){
return new PS4Game();
}
return null;
}
}
package com.haozi.design.pattern.creational.simplefactory;
public class Test {
public static void main(String[] args) {
Game game = new PCGame();
game.buy();
game = new PS4Game();
game.buy();
}
}
这样,应用层直接和工厂类打交道,不需要创建具体细节,当然开始的时候也说了,这种结构如果想扩展一个game,需要在工厂类中加判断,这样的方式也不符合开闭原则,所以还可以用反射的方式改进:
public class GameFactory {
public Game getGame(Class c){
Game game = null;
try {
game = (Game) Class.forName(c.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return game;
}
}
public class Test {
public static void main(String[] args) {
GameFactory gameFactory = new GameFactory();
Game game = gameFactory.getGame(PCGame.class);
if (game != null){
game.buy();
}
}
}
这样,再进行扩展的时候就不需要再修改工厂类了,当然,这种做法也不是非常完美的。
用到设计模式的源码:
不需要太多解释了,这里只提一点,方法用的是static,是因为这里不需要进行扩展了。
工厂模式
定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个对象,工厂模式让对象的实例化推迟到子类中进行。
类型:创建型
使用场景:创建对象需要大量重复的代码,客户端(应用层)不依赖于产品类如何被创建、实现等细节。一个类通过其子类来决定创建哪个对象
优点:用户只需要关心所需产品对应的工厂,无需关心创建细节。加入新产品符合开闭原则,提高扩展性。
缺点:类的个数容易过多,增加复杂度。增加了系统的抽象性和理解难度。
例子:
public abstract class Game {
public abstract void buy();
}
public class PCGame extends Game {
@Override
public void buy() {
System.out.println("从stream上买游戏");
}
}
public class PS4Game extends Game {
@Override
public void buy() {
System.out.println("从ps store上买游戏");
}
}
public interface GameFactory {
Game getGame();
}
public class PCGameFactory implements GameFactory {
@Override
public Game getGame() {
return new PCGame();
}
}
public class PS4GameFactory implements GameFactory {
@Override
public Game getGame() {
return new PS4Game();
}
}
public class Test {
public static void main(String[] args) {
GameFactory factory = new PCGameFactory();
Game game = factory.getGame();
game.buy();
}
}
这样,应用层test直接和gamefactory打交道,具体创建什么实例直接用对应的factory就好,从扩展性来说,新加一个游戏,加一个对应的GameFactory和一个对应的Game实现类,扩展性好,但加的东西有点多。
用到设计模式的源码:
Collection中
通过查找实现类可以找到ArrayList
根据工厂方法的类图,我画了一个简单的
再看一个:
URLStreamHandlerFactory
找一下实现类
可以看出实现关系,他们的返回值URLStreamHandler也有很多实现类
抽象工厂
抽象工厂提供了一个创建一系列相关或相互依赖对象的接口,无需指定他们具体的类。抽象工厂可以把具有同一主题的工厂封装起来,客户端程序创建抽象工厂的具体实现,然后使用抽象工厂的实现来创建接口工厂,客户端不需要知道使用的具体工厂的类型。
类型:创建型
使用场景:客户端(应用层)不依赖于产品类如何被创建、实现等细节。强调一系列相关的产品对象(同一产品族)一起使用创建对象需要大量重复的代码。提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
关于产品族和产品等级:
举个例子,美的有空调、洗衣机、冰箱,海尔也有空调、洗衣机、冰箱。
产品族:美的空调、美的洗衣机、美的冰箱都属于美的,是同一个产品族。
产品等级:美的空调、海尔空调都是空调,属于一个产品等级结构。
优点:具体产品在应用层代码隔离,无需关心创建细节。将一个系列的产品族统一到一起创建。
缺点:规定了所有可能被创建的产品集合,产品族中扩展新的产品比较困难,需要修改抽象工厂的接口。增加了系统的抽象性和理解难度。
例子:
上面的游戏的例子,游戏可以分为电脑游戏和ps4游戏等,现在需要新加一个需求,电脑和ps4不光有游戏,还要有别的软件例如播放器,那以上面工厂方法的例子,还需要新建播放器工厂、电脑播放器、ps4播放器等,这种情况下,用抽象工厂来试试:
public interface SoftwareFactory {
Game initGame();
MusicPlayer initMusicPlayer();
}
public abstract class Game {
public abstract void play();
}
public abstract class MusicPlayer {
public abstract void play();
}
public class PCGame extends Game {
@Override
public void play() {
System.out.println("玩电脑游戏");
}
}
public class PS4Game extends Game {
@Override
public void play() {
System.out.println("玩PS4游戏");
}
}
public class PCMusicPlayer extends MusicPlayer {
@Override
public void play() {
System.out.println("听电脑播放器的歌");
}
}
public class PS4MusicPlayer extends MusicPlayer {
@Override
public void play() {
System.out.println("听PS4的歌");
}
}
public class PCSoftwareFactory implements SoftwareFactory {
@Override
public Game initGame() {
return new PCGame();
}
@Override
public MusicPlayer initMusicPlayer() {
return new PCMusicPlayer();
}
}
public class PS4SoftwareFactory implements SoftwareFactory {
@Override
public Game initGame() {
return new PS4Game();
}
@Override
public MusicPlayer initMusicPlayer() {
return new PS4MusicPlayer();
}
}
public class Test {
public static void main(String[] args) {
SoftwareFactory softwareFactory = new PCSoftwareFactory();
Game game = softwareFactory.initGame();
MusicPlayer musicPlayer = softwareFactory.initMusicPlayer();
game.play();
musicPlayer.play();
SoftwareFactory softwareFactory1 = new PS4SoftwareFactory();
Game game1 = softwareFactory1.initGame();
MusicPlayer musicPlayer1 = softwareFactory1.initMusicPlayer();
game1.play();
musicPlayer1.play();
}
}
在产品族产品很多的情况下,用抽象工厂是非常合适的,因为如果用普通工厂,人为的编码不排除出现PCGame和PS4MusicPlayer组合的可能性,而用抽象工厂,应用层直接和SoftwareFactory打交道,不直接和应用打交道,只需要知道是哪种类型的就好。所以如果我需要新增一个产品族,那么我新建对应的软件工厂和具体实现就好,非常方便。但是,当然缺点也一目了然,这种模式下新建一个产品等级,比如新建一个其他软件,那修改的地方可就非常多了。
所以总结一下,抽象工厂关注产品族,工厂模式关注产品等级结构。
用到设计模式的源码:
Connection中
从这三个方法可以看出,返回的都是同一个产品族,比如创建一个mysql的connection,那么返回的肯定都是mysql的statement。
再看Statement
这也是一个抽象工厂,再比如