1、创建型模式-工厂模式
⼯⼚模式也称简单⼯⼚模式,是创建型设计模式的⼀种,这种设计模式提供了按需创建对象的最佳⽅式。
同时,这种创建⽅式不会对外暴露创建细节,并且会通过⼀个统⼀的接⼝创建所需对象。
工厂模式属于创建型模型的一种,其目的就是隐藏创建类的细节与过程。
(1)场景
我是一个准大一计算机专业学生,电脑小白,开学要买电脑,但是不懂电脑配置的任何相关信息,无从下手~
(2)未使用工厂模式
电脑接口:
public interface Computer {
public String describe();
}
外星⼈游戏笔记本:
public class Alienware implements Computer{
@Override
public String describe() {
return "外星⼈ALIENWARE m15 R7 15.6英⼨⾼端游戏本 12代i7 32G RTX3060 QHD 240Hz ⾼刷屏 轻薄笔记本电脑2765QB";
}
}
⾼性能独显PC主机:
public class Desktop implements Computer{
@Override
public String describe() {
return "外星⼈ALIENWARE R13 ⽔冷电竞游戏⾼端台式机 第12代i7 32G 512GB+2T RTX3070 办公台式机 9776W";
}
}
Macbook办公轻薄本:
public class Macbook implements Computer{
@Override
public String describe() {
return "Apple MacBook Pro 13.3 ⼋核M1芯⽚ 8G 256G SSD 深空灰 笔记本电 脑 轻薄本 MYD82CH/A";
}
}
2U戴尔服务器:
public class Server implements Computer{
@Override
public String describe() {
return "戴尔(DELL) R740⼁R750⼁2U机架式服务器主机双路GPU深度学习 R740〖1 *银牌4210R 10核20线程〗 8G内存⼁1TB SATA硬盘 ⼁H350戴尔(DELL)";
}
}
以上分析,所有电脑放在店里,顾客进店后只知道自己的需求,不知道店里各种规格和型号的电脑哪个适合自己,这种需要让客户自己了解细节才能决定结果的,从设计层面上讲就破坏了“迪米特法则”,抉择权应该在电脑店,而不是顾客自己手里,目前的电脑和顾客的关系图就如下所示,顾客知道的太多,不能够做出抉择:
(3)使用工厂模式
这个时候我们需要一个工厂类,让顾客只知道自己的需求,不用了解电脑的规格和参数就能选出一个参考的电脑来:
增加一个导购员帮助用户实现抉择,根据用户的不同需求提供不同的电脑产品,这就相当于隐藏了用户对电脑配置和规格的抉择,只需要关注自己的需求即可完成电脑的选择。
「3.1」代码实现
导购员就充当了“工厂”的角色:
public class ShopAssistant {
public Computer suggest(String purpose){
Computer computer = null;
if(purpose.equals("⽹站建设")){
return new Server();
}else if(purpose.equals("电竞⽐赛")){
return new Desktop();
}else if(purpose.equals("⽇常办公")){
return new Macbook();
}else if(purpose.equals("3A游戏")){
return new Alienware();
}
return computer;
}
}
此时用户只关注自己的需求,将其告诉导购员即可获取对应推荐的电脑:
public class Customer {
public static void main(String[] args) {
List<String> purposes = Arrays.asList("网站建设", "电竞比赛", "日常办公","3A游戏");
//随机获取业务名称,模拟顾客需求
String purpose = purposes.get((int) (Math.random() * 4));
ShopAssistant shopAssistant = new ShopAssistant();
Computer c = shopAssistant.suggest(purpose);
System.out.println(c.describe());
}
}
工厂就是通过if…else来创建对象,当然对于if…else也可以进行优化,例如使用反射或者初始化就不用多层if…else。
总之工厂的特点就是:提供方法根据需求来返回结果。
使用反射和初始化完成形状工厂例子:
public interface ShapeFactory {
//多例对象
enum SHAPEUTILS {
CIRCLE(Circle.class),
DIAMOND(Diamond.class);
public Class shapeClass;
SHAPEUTILS(Class shapeClass) {
this.shapeClass = shapeClass;
}
/**
* 通过形状编码获取指定形状的新类
*
* @param shapeClass shape接口的子类字节码类
* @return 形状接口实例对象
*/
public static Shape getShapeByShapeClass(Class shapeClass) {
try {
return (Shape) shapeClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
//单例对象
enum ShapeFactoryUtils {
CIRCLE("CIRCLE", new Circle()),
DIAMOND("DIAMOND", new Diamond());
public String shapeCode;
public Shape shape;
public static Map<String, Shape> shapeLookup = new HashMap<>();
ShapeFactoryUtils(String shape, Shape shapeClass) {
this.shapeCode = shape;
this.shape = shapeClass;
}
static {
for (ShapeFactoryUtils shape : ShapeFactoryUtils.values()) {
shapeLookup.put(shape.shapeCode, shape.shape);
}
}
/**
* 通过形状编码获取指定形状的新类
*
* @param shapeCode 形状编码
* @return 指定形状类
*/
public static Shape getShapeByShapeCode(String shapeCode) {
return shapeLookup.get(shapeCode);
}
}
}
「3.2」项目中的应用
例如可销售给不同地区、国家的项目,可能需要不同语言来显示系统字体,是需要让用户自己选择字体然后登陆系统?
这样以来Client就知道系统国际化的细节,知道有支持多少种语言,也是违背“迪米特法则”的,所以可以用“工厂模式”进行改进。
还是通过改进自动识别?
2、创作型模式-抽象工厂模式
通过构建顶层的抽象⼯⼚和抽象的产品,屏蔽系列产品的构建过程。
抽象工厂其实很好理解,它其实就是其他工厂的工厂,它可以在抽象工厂中创建出其他工厂,与工厂模式一样,都是来解决接口选择问题,同样属于创建型模式。
(1)场景
公司的一个项目早期接入七牛云OSS上传图片与视频,后来因为业务调整,公司要求额外支持阿里云、腾讯云等其他云服务商,并且可以对客户提供外界访问。
(2)代码实现
任何OSS对外提供的功能基本上是一样的,但是针对每个厂商可能有自己的特色,例如七牛云是免费的,但是针对于图片或视频提供的清晰度优化并不高,像阿里云、腾讯云等等OSS是付费的,就可以为提供优化为更高清晰度的图片和视频。
「2.1」公共抽象
使用抽象工厂将都具备的功能给抽象出来,构建顶层的抽象⼯⼚和抽象的产品,隐藏具体实现细节。
图片接口:
public interface OssImage {
/**
* 获取缩略图
* @return 展示地址
*/
String getThumb();
/**
* 获取带的水印图片
* @return 展示地址
*/
String getWatermark();
/**
* 获取增强清晰度的图片
* @return 展示地址
*/
String getEnhance();
}
视频接口:
public interface OssVideo {
/**
* 获取高清视频
* @return 播放地址
*/
String get720P();
/**
* 获取2k视频
* @return 播放地址
*/
String get1080P();
}
抽象工厂:
public interface AbstractOssFactory {
/**
* 上传图片
*
* @param bytes 图片
* @return 展示地址
*/
OssImage uploadImage(byte[] bytes);
/**
* 上传视频
*
* @param bytes 视频
* @return 播放地址
*/
OssVideo uploadVideo(byte[] bytes);
}
「2.2」产品细节实现
不同的产品,实现细节是不同的,例如阿里云和七牛云的图片缩略图的尺寸默认生产尺寸不同、阿里云额外支持图片透明背景、更清晰的图片和更高码率的视频,而通过对工厂产品的封装,再将产品交给工厂,用户直接使用工厂即可,并不用关心产品实现细节。
阿里云上传图片细节实现:
public class AliyunOssImage implements OssImage {
private byte[] bytes;
/**
* 阿里云上传图片实现细节
*
* @param bytes 图片
* @param watermark 水印内容
* @param transparent 是否生成透明背景
*/
public AliyunOssImage(byte[] bytes, String watermark, boolean transparent) {
this.bytes = bytes;
//模拟上传成功
System.out.println("[阿⾥云]图⽚已上传⾄阿⾥云OSS,URL:http://oss.aliyun.com/xxxxxxx.jpg");
System.out.println("[阿⾥云]已⽣成缩略图,尺⼨640X480像素");
System.out.println("[阿⾥云]已为图⽚新增⽔印,⽔印⽂本:" + watermark + ",⽂本颜⾊:#aaaaaa,背景透明:" + transparent);
System.out.println("[阿⾥云]已将图⽚AI增强为4K极清画质");
}
//模拟上传成功后返回的地址
@Override
public String getThumb() {
return "http://oss.aliyun.com/xxxxxxx_thumb.jpg";
}
//模拟上传成功后返回的地址
@Override
public String getWatermark() {
return "http://oss.aliyun.com/xxxxxxx_watermark.jpg";
}
//模拟上传成功后返回的地址
@Override
public String getEnhance() {
return "http://oss.aliyun.com/xxxxxxx_enhance.jpg";
}
}
阿里云上传视频细节实现:
public class AliyunOssVideo implements OssVideo {
private byte[] bytes;
/**
* 阿里云上传视频实现细节
* @param bytes 视频
* @param watermark 水印内容
*/
public AliyunOssVideo(byte[] bytes, String watermark) {
this.bytes = bytes;
//模拟上传视频成功
System.out.println("[阿⾥云]视频已上传⾄阿⾥云OSS,URL:http://oss.aliyun.com/xxx.mp4");
System.out.println("[阿⾥云]720P转码成功,码率:5000K");
System.out.println("[阿⾥云]1080P转码成功,码率:7000K");
}
//模拟上传成功后返回的地址
@Override
public String get720P() {
return "http://oss.aliyun.com/xxx_720p.mp4";
}
//模拟上传成功后返回的地址
@Override
public String get1080P() {
return "http://oss.aliyun.com/xxx_1080p.mp4";
}
}
七牛云上传图片实现细节:
public class QiniuOssImage implements OssImage {
private byte[] bytes;
/**
* 七牛云上传图片实现细节
* @param bytes 图片
* @param watermark 水印内容
*/
public QiniuOssImage(byte[] bytes, String watermark) {
this.bytes = bytes;
//模拟上传成功
System.out.println("[七⽜云]图⽚已上传⾄七⽜云OSS,URL:http://oss.qini u.com/xxxxxxx.jpg");
System.out.println("[七⽜云]已⽣成缩略图,尺⼨800X600像素");
System.out.println("[七⽜云]已为图⽚新增⽔印,⽔印⽂本:" + watermark + ",⽂本颜⾊#cccccc");
System.out.println("[七⽜云]已将图⽚AI增强为1080P⾼清画质");
}
//模拟上传成功后返回的地址
@Override
public String getThumb() {
return "http://oss.qiniu.com/xxxxxxx_thumb.jpg";
}
//模拟上传成功后返回的地址
@Override
public String getWatermark() {
return "http://oss.qiniu.com/xxxxxxx_watermark.jpg";
}
//模拟上传成功后返回的地址
@Override
public String getEnhance() {
return "http://oss.qiniu.com/xxxxxxx_enhance.jpg";
}
}
七牛云上传视频实现细节:
public class QiniuOssVideo implements OssVideo {
private byte[] bytes;
/**
* 七牛云上传视频实现细节
* @param bytes 视频
* @param watermark 水印内容
*/
public QiniuOssVideo(byte[] bytes, String watermark) {
this.bytes = bytes;
System.out.println("[七⽜云]视频已上传⾄阿⾥云OSS,URL:http://oss.qiniu.com/xxx.mp4");
System.out.println("[七⽜云]720P转码成功,码率:2500K");
System.out.println("[七⽜云]1080P转码成功,码率:3500K");
}
//模拟上传成功后返回的地址
@Override
public String get720P() {
return "http://oss.qiniu.com/xxx_720p_2500.mp4";
}
//模拟上传成功后返回的地址
@Override
public String get1080P() {
return "http://oss.qiniu.com/xxx_1080p_3500.mp4";
}
}
「2.3」工厂实现
通过实现抽象工厂创造出不同的工厂,来提供不同的实现方法,工厂方法来进行细节的实现。
阿里云OSS工厂:
public class AliyunOssFactory implements AbstractOssFactory {
/**
* 上传图片
*
* @param bytes 图片
* @return 展示链接
*/
@Override
public OssImage uploadImage(byte[] bytes) {
return new AliyunOssImage(bytes, "抽象工厂模式", true);
}
/**
* 上传视频
*
* @param bytes 视频
* @return 播放链接
*/
@Override
public OssVideo uploadVideo(byte[] bytes) {
return new AliyunOssVideo(bytes, "抽象工厂模式");
}
}
七牛云OSS工厂:
public class QiniuOssFactory implements AbstractOssFactory {
/**
* 上传图片
* @param bytes 图片
* @return 展示地址
*/
@Override
public OssImage uploadImage(byte[] bytes) {
return new QiniuOssImage(bytes, "抽象工厂模式");
}
/**
* 上传视频
* @param bytes 视频
* @return 播放地址
*/
@Override
public OssVideo uploadVideo(byte[] bytes) {
return new QiniuOssVideo(bytes, "抽象工厂模式");
}
}
「2.4」业务代码使用OSS工厂
可以看到,通过抽象工厂对OSS的顶层产品进行抽象,具体产品工厂实现抽象工厂,不同细节对于抽象工厂是隐藏的,在使用时我们只关系使用的是哪个产品工厂,即可完成相应功能的实现,但是不同的工厂的产品特质可能也不同(例如不同清晰度的图片和不同码率的视频等)。
public class AbstractOssFactoryTest {
public static void main(String[] args) {
AbstractOssFactory factory = new QiniuOssFactory();
OssImage ossImage = factory.uploadImage(new byte[1024]);
OssVideo ossVideo = factory.uploadVideo(new byte[1024]);
System.out.println(ossImage.getThumb());
System.out.println(ossImage.getWatermark());
System.out.println(ossImage.getEnhance());
System.out.println(ossVideo.get720P());
System.out.println(ossVideo.get1080P());
System.out.println("===========================================");
AbstractOssFactory factory2 = new AliyunOssFactory();
OssImage ossImage2 = factory2.uploadImage(new byte[1024]);
OssVideo ossVideo2 = factory2.uploadVideo(new byte[1024]);
System.out.println(ossImage2.getThumb());
System.out.println(ossImage2.getWatermark());
System.out.println(ossImage2.getEnhance());
System.out.println(ossVideo2.get720P());
System.out.println(ossVideo2.get1080P());
}
}
即使以后仍要对接其他)SS厂商,我们只关系其他OSS服务的实现细节,然后创建工厂实现抽象工厂,即可完成OSS的对接服务。这就符合“开闭原则”,针对于原有类、对象、模块、函数等的扩展是开放的,并没有去进行修改。
3、创造型模式-单例模式
//TODO
4、创造型模式-构建者模式
//TODO
5、创造型模式-原型模式
//TODO