基于接口而非实现编程

“基于接口而非实现编程”这条原则的另一个表述方式,是“基于抽象而非实现编程”。后者的表述方式其实更能体现这条原则的设计初衷。在软件开发中,最大的挑战之一就是需求的不断变化,这也是考验代码设计好坏的一个标准。越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性,越能应对未来的需求变化。好的代码设计,不仅能应对当下的需求,而且在将来需求发生变化的时候,仍然能够在不破坏原有代码设计的情况下灵活应对。而抽象就是提高代码扩展性、灵活性、可维护性最有效的手段之一。



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);

  }

}

尽管我们通过接口来隔离了两个具体的实现。但是,在项目中很多地方,我们都是通过下面第 8 行的方式来使用接口的。这就会产生一个问题,那就是,如果我们要替换图片存储方式,还是需要修改很多类似第 8 行那样的代码。这样的设计还是不够完美,对此,你有更好的实现思路吗?

// 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);

  }


两种方法改进:简单工厂方法和使用反射。

1、简单工厂方法

ImageStore imageStore = ImageStoreFactory.newInstance(SOTRE_TYPE_CONFIG);

config文件可以写类似properties的文件,使用key-value存储。

缺点:再新增另一种存储手段时,需要修改工厂类和添加新的类。修改工厂类,违反了开放-封闭原则。

那有没有更好一点的方法呢?

2、使用反射。

在配置文件中定义需要的image store类型。

在ProcessJob中

ImageStore store = (ImageStore) Class.forName(STORE_CLASS)

    .newInstance();

缺点:使用反射,在大量创建对象时会有性能损失。

你可能感兴趣的:(基于接口而非实现编程)