从王者荣耀看设计模式(抽象工厂模式)
一.简介
王者荣耀是一款流行的大众手机竞技游戏,目前已经来到了第18个赛季!支持在多种操作平台上运行是王者荣耀如此火爆的原因之一。玩家不仅能通过Andriod手机登陆游戏并且操作游戏,还能在IOS手机上登陆、操作游戏
模式动机
抽象工厂模式是工厂方法模式的泛化版,工厂方法模式是一种特殊的抽象工厂模式。在工厂方法模式中,每一个具体工厂只能生产一种具体产品,而在抽象工厂方法模式中,每一个具体工厂可以生产多个具体产品。在本实例中,苹果工厂生产的产品支持登陆和操作IOS游戏。Andriod工厂生产的产品支持登陆和操作Andriod游戏
二.产品等级结构与产品族
抽象工厂模式是工厂模式的泛化版,工厂方法模式是一种特殊的抽象工厂模式。在工厂方法模式中,每一个具体工厂只能生成一种具体产品,而在抽象工厂模式中,每一个具体工厂可以生产多个具体产品。为了清晰地理解抽象工厂模式,需要首先明确两个概念:
1. 产品等级结构(一类):产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCl电视机,则抽象电视机与具体电视机之间构成产品等级结构,抽象电视机是父类,而具体品牌的电视机是子类。
2. 产品族(一族):在抽象工厂模式中,产品族是指由同一工厂生产的,位于不同产品等级结构中的一组产品,如海尔工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中
当抽象工厂模式中每一个具体类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象方法与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化为简单工厂模式
三.抽象工厂模式
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式
-
抽象工厂模式应用场景
在一下情况下可以使用抽象工厂模式
⑴. 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。用户无需关心对象的创建过程,将对象的创建和使用解耦。
⑵. 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加产品族。
⑶. 属于同一产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来,同一产品族的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束。
⑷. 系统提供一个产品类的库,所有产品以同样的接口出现,从而使客户端不依赖于具体实现。对于这些产品,客户只需要知道他们提供了哪些具体的业务方法,而不需要知道这些对象的创建过程。在客户端代码中针对抽象编程,而将具体类写入配置文件中。 -
抽象工厂模式涉及的设计原则有:
★依赖于抽象,不依赖于具体类
★对于扩展是开放的,对于修改是封闭的
★封装变化
★针对接口编程,不针对实现编程 -
抽象工厂模式的通用类图:
-
抽象工厂模式涉及的角色有:
● AbstractFactory(抽象工厂)
抽象工厂用于声明生成抽象产品的方法,在一个抽象工厂中可以定义一组方法,每一个方法对应一个产品等级结构。
● ConcreteFactory(具体工厂)
具体工厂实现了抽象工厂声明的生成抽象产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
● AbstractProduct(抽象产品)
抽象产品为每种产品声明接口,在抽象产品中定义了产品的抽象业务方法。
● ConcreteProduct(具体产品)
具体产品定义具体工厂生产的具体产品对象,实现抽象产品接口中定义的业务方法。 -
抽象工厂模式的优点:
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离。更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到广泛的应用
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一个非常实用的设计模式
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合"开闭原则"
- 抽象工厂模式的缺点:
在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
我思故我在:抽象工厂模式的优点中提到增加新的具体工厂和产品族很方便,无须修改已有系统,符合"开闭原则",
在抽象工厂模式的缺点中又说在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品:以本blog实例来说,当我们支持Andriod和IOS都可以玩王者荣耀的系统已经设计实现完毕,这个时候发现市场上有一个新的潜在的手机操作系统有很大的用户源,我们只需要添加一个××GameFactory实现抽象工厂GameFactory,在实现LoginGame接口与PlayGame接口中的具体方法就好ᕦ(・ㅂ・)ᕤ。添加新的产品对象添加类似于LoginGame与PlayGame的类,这个需要不仅修改具体工厂类,还需要修改抽象工厂类。(╬ ̄皿 ̄)=○
四.结构图
五.设计类图
六.代码实现
创建抽象产品类LoginGame
package com.practice.LoginGame;
/*
* 抽象产品类
*/
public interface LoginGame {
public void Login();
}
创建具体产品类AndriodLogin
package com.practice.LoginGame;
/*
* 具体产品类,安卓操作系统登陆游戏
*/
public class AndriodOSLogin implements LoginGame{
public void Login() {
System.out.println("安卓系统登陆王者荣耀……");
}
}
创建具体产品类IOSLogin
package com.practice.LoginGame;
/*
* 通过IOS操作系统登陆王者荣耀
*/
public class IOSLogin implements LoginGame{
public void Login() {
System.out.println("苹果系统登陆王者荣耀……");
}
}
创建抽象产品类PlayGame
package com.practice.PlayGame;
/*
* 创建抽象产品类
*/
public interface PlayGame {
public void Play();
}
创建具体产品类AndriodPlay
package com.practice.PlayGame;
/*
* 创建具体产品类:通过安卓手机玩王者荣耀
*/
public class AndriodOSPlay implements PlayGame {
public void Play() {
System.out.println("安卓系统开始王者荣耀游戏!");
}
}
创建具体产品类IOSPlay
package com.practice.PlayGame;
/*
* 创建具体产品类:通过苹果手机玩王者荣耀
*/
public class IOSPlay implements PlayGame {
public void Play() {
System.out.println("苹果系统开始王者荣耀游戏!");
}
}
创建抽象工厂类GameFactory
package com.practice.Factory;
import com.practice.LoginGame.LoginGame;
import com.practice.PlayGame.PlayGame;
/*
* 创建抽象工厂类
*/
public interface GameFactory {
public LoginGame supportLoginGame();
public PlayGame supportPlayGame();
}
创建具体工厂类AndriodGameFactory
package com.practice.Factory.Imple;
import com.practice.Factory.GameFactory;
import com.practice.LoginGame.AndriodOSLogin;
import com.practice.LoginGame.LoginGame;
import com.practice.PlayGame.AndriodOSPlay;
import com.practice.PlayGame.PlayGame;
/*
* 创建具体工厂类
*/
public class AndriodGameFactory implements GameFactory {
public LoginGame supportLoginGame() {
return new AndriodOSLogin();
}
public PlayGame supportPlayGame() {
return new AndriodOSPlay();
}
}
创建具体工厂类IOSGameFactory
package com.practice.Factory.Imple;
import com.practice.Factory.GameFactory;
import com.practice.LoginGame.IOSLogin;
import com.practice.LoginGame.LoginGame;
import com.practice.PlayGame.IOSPlay;
import com.practice.PlayGame.PlayGame;
/*
* 创建具体工厂类:通过IOS工厂生成具体产品实例
*/
public class IOSGameFactory implements GameFactory {
public LoginGame supportLoginGame() {
return new IOSLogin();
}
public PlayGame supportPlayGame() {
return new IOSPlay();
}
}
配置文件Config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<className>IOSGameFactory</className>
</config>
工具类XMLUtilGame读取XML文件内容
package com.practice.Client;
import java.io.File;
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class XMLUtilGame {
//该方法用于从XML配置文件中提取具体类类名。并返回一个实例对象
public static Object getGameFactory() {
try {
//创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src\com\practice\Client\config.xml"));
//获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName("className");
Node classNode = nl.item(0).getFirstChild();
String gameFactory = classNode.getNodeValue();
//System.out.println(gameFactory);
//通过类名生成实例对象并将其返回
String ClassName = "com.practice.Factory.Imple."+ gameFactory;
Class<?> c = Class.forName(ClassName);
@SuppressWarnings("deprecation")
Object obj = c.newInstance();
return obj;
}catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
测试代码Client
package com.practice.Client;
import com.practice.Factory.GameFactory;
import com.practice.LoginGame.LoginGame;
import com.practice.PlayGame.PlayGame;
public class Client {
public static void main(String [] args) {
GameFactory factory;
LoginGame lg;
PlayGame pg;
factory = (GameFactory) XMLUtilGame.getGameFactory();
lg = factory.supportLoginGame();
lg.Login();
pg = factory.supportPlayGame();
pg.Play();
}
}
运行结果: