【设计模式】让你彻底搞懂接口、工厂模式、反射

引言

上篇文章我们讨论了设计模式在软件开发中的重要性,今天我们就来做个实际的例子展示一下设计模式的魅力。在日常开发中经常会遇到某种实现的不同选择问题,如图片上传可能分:阿里云上传和私有云上传,客户端的图片展示也可能分:Fresco和Glide。当然我们可以切换的时候来进行大量的修改来达到目的,但其实有更优雅的方法来实现兼容多种具体实现的方案。

最开始的写法

我们就拿图片上传举例,比如最开始的时候没考虑太多就直接使用了阿里云作为图片存储的地方,那么你可能会写出下面的代码。

public class AliyunImageStore { //... 省略属性、构造函数等...
	public void createBucketIfNotExisting(String bucketName) { // ... 创建 bucket 代码逻辑...
	public String generateAccessToken() {
		// ... 根据 accesskey/secrectkey 等生成 access token
	}
	public String uploadToAliyun(Image image, String bucketName, String accessToken) {
		//... 上传图片到阿里云...
		//... 返回图片存储在阿里云上的地址 (url)...
	}
	public Image downloadFromAliyun(String url, String accessToken) { 
		//... 从阿里云下载图片...
	}
	// AliyunImageStore 类的使用举例 public class ImageProcessingJob {
	private static final String BUCKET_NAME = "ai_images_bucket"; //... 省略其他无关代码...
	public void process() {
		Image image = ...; // 处理图片,并封装为 Image 对象
		AliyunImageStore imageStore = new AliyunImageStore(/* 省略参数 */);
		imageStore.createBucketIfNotExisting(BUCKET_NAME);
		String accessToken = imageStore.generateAccessToken();
		imagestore.uploadToAliyun(image, BUCKET_NAME, accessToken);
	} 
}

整个上传流程包含三个步骤:创建 bucket(你可以简单理解为存储目录)、生成 access token 访问凭证、携带 access token 上传图片到指定的 bucket 中。代码实现非常简单, 类中的几个方法定义得都很干净,用起来也很清晰,乍看起来没有太大问题,完全能满足我 们将图片存储在阿里云的业务需求。
不过,软件开发中唯一不变的就是变化。过了一段时间后,我们自建了私有云,不再将图片 存储到阿里云了,而是将图片存储到自建私有云上。为了满足这样一个需求的变化,解决这个问题的根本方法就是,在编写代码的时候,要遵 从“基于接口而非实现编程”的原则,我们可能会这么改:

public interface ImageStore {
	String upload(Image image, String bucketName);
	Image download(String url);
}
public class AliyunImageStore implements ImageStore {
	//... 省略属性、构造函数等...
	public String upload(Image image, String bucketName) {
		createBucketIfNotExisting(bucketName);
		String accessToken = generateAccessToken();
		//... 上传图片到阿里云...
		//... 返回图片在阿里云上的地址 (url)... 
	}
	public Image download(String url) {
		String accessToken = generateAccessToken(); 
		//... 从阿里云下载图片...
	}
	private void createBucketIfNotExisting(String bucketName) { 
		// ... 创建 bucket...
		// ... 失败会抛出异常..
	}
	private String generateAccessToken() {
		// ... 根据 accesskey/secrectkey 等生成 access token
	}
}
// 上传下载流程改变:私有云不需要支持 access token
public class PrivateImageStore implements ImageStore {
	public String upload(Image image, String bucketName) {
		createBucketIfNotExisting(bucketName);
		//... 上传图片到私有云...
		//... 返回图片的 url...
	}
	public Image download(String url) { 
		//... 从私有云下载图片...
	}
	private void createBucketIfNotExisting(String bucketName) { 
		// ... 创建 bucket...
		// ... 失败会抛出异常..
	}
}
// ImageStore 的使用举例
public class ImageProcessingJob {
	private static final String BUCKET_NAME = "ai_images_bucket"; 
	//... 省略其他无关代码...
	public void process() {
	Image image = ...;
	// 处理图片,并封装为 Image 对象 
	ImageStore imageStore = new PrivateImageStore(...); 
	imagestore.upload(image, BUCKET_NAME);
}

工厂模式

我们通过接口来隔离了两个具体的实现。但如果我们还要替换图片存储方式,还是需要修改很多类似接口 = new 具体类;那样的代码。这样的设计还是不够完美,因此我们可以尝试使用工厂模式 + 配置文件的方式去做,代码如下。

public interface ImageStore {
  String upload(Image image, String bucketName);

  Image download(String url);
}
public class AliyunImageStore implements ImageStore {
  public AliyunImageStore() {
  }
  @Override
  public String upload(Image image, String bucketName) {
    createBucketInfNotExisting(bucketName);
    String accessToken = generateAccessToken();
    System.out.println("AliyunImageStore upload");
    return null;
  }
  @Override
  public Image download(String url) {
    return null;
  }
  private void createBucketInfNotExisting(String bucketName) {
  }
  private String generateAccessToken() {
    return null;
  }
}
public class PrivateImageStore implements ImageStore {
  public PrivateImageStore() {
  }
  @Override
  public String upload(Image image, String bucketName) {
    createBucketInfNotExisting(bucketName);
    System.out.println("PrivateImageStore upload");
    return null;
  }
  @Override
  public Image download(String url) {
    return null;
  }
  public void createBucketInfNotExisting(String bucketName) {
  }
}
//工厂类
public class ImageStoreFactory {
  private static final String PRIVATE = "private";
  private static final String ALIYUN = "aliyun";
  private ImageStoreFactory() {}
  public static ImageStore newInstance(String storeType) {
    switch (storeType) {
      case PRIVATE:
        return new PrivateImageStore();
      case ALIYUN:
        return new AliyunImageStore();
      default:
        throw new IllegalArgumentException("not implemented yet");
    }
  }
}
public class ImageProcessingJob {
  private static final String BUCKET_NAME = "ai_images_bucket";
  private static final String STORE_TYPE = "STORE_TYPE";
  private static final String PROP_PATH = "./config.properties";
  public void process() {
    Image image = null;
	//String storeType = prop.getString(STORE_TYPE);
    String storeType = FileUtils.load(PROP_PATH)
        .getProperty(STORE_TYPE);
    ImageStore imageStore = ImageStoreFactory.newInstance(storeType);
    imageStore.upload(image, BUCKET_NAME);
  }
  public static void main(String[] args) {
    ImageProcessingJob job = new ImageProcessingJob();
    job.process();
  }
}
//config.properties 配置文件
STORE_TYPE=private

反射

是不是发现代码优雅了很多,其实工厂类也可以用反射的方式去实现,代码如下:

public class ImageProcessingJob {
  private static final String BUCKET_NAME = "ai_images_bucket";
  private static final String STORE_CLASS = "STORE_CLASS";
  private static final String PROP_PATH = "./config.properties";
  public void process() {
    Image image = null;
    try {
      String storeClass = FileUtils.load(PROP_PATH)
          .getProperty(STORE_CLASS);
      ImageStore imageStore = (ImageStore) Class.forName(storeClass).newInstance();
      imageStore.upload(image, BUCKET_NAME);
    } catch (Exception e) {
      e.printStackTrace();
    }

  }

  public static void main(String[] args) {
    ImageProcessingJob job = new ImageProcessingJob();
    job.process();
    // PrivateImageStore upload
  }
}
//config.properties 配置文件
STORE_CLASS=PrivateImageStore

总结

通过图片上传这个实际场景的例子,相信你对接口、工厂模式、反射都有了比较好的理解。
喜欢请关注👀、点赞👍、评论👯‍♀️
微信公众号:Peachou
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比特桃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值