【问题标题】:Decoupling client code from library将客户端代码与库解耦
【发布时间】:2015-03-12 17:26:02
【问题描述】:

我们有一个与 Sanselan 库紧密耦合的图像实用方法,现在我需要添加第二个库(元数据提取器)来尝试读取图像的元信息,以防 Sanselan 无法做到。

我说我们的代码与 Sanselan 紧密耦合,因为我们有多次直接调用 Sanselan 的静态方法或通过这些方法之一返回对象的 getter(用 // Sanselan 注释的行)

BufferedImage toBufferedImageJpeg(byte[] image) throws ImageReadException, IOException
    {
        ImageInfo imageInfo = Sanselan.getImageInfo(image); // Sanselan
        if (isImageTooBig(imageInfo.getHeight(), imageInfo.getWidth())) // Sanselan
        {
            throw new IllegalArgumentException("Image is too big");
        }
        if (imageInfo.getFormat().equals(ImageFormat.IMAGE_FORMAT_JPEG)) // Sanselan
        {
            if (ImageInfo.COLOR_TYPE_CMYK == imageInfo.getColorType()) // Sanselan
            {
                ICC_Profile iccProfile = Sanselan.getICCProfile(image); // Sanselan
                ImageInputStream imageInputStream = ImageIO.createImageInputStream(new ByteArrayInputStream(image));
                try
                {
                    Iterator<ImageReader> readers = ImageIO.getImageReaders(imageInputStream);
                    if (false == readers.hasNext())
                    {
                        return null;
                    }

                    ImageReader reader = readers.next();
                    reader.setInput(imageInputStream);

                    WritableRaster raster = (WritableRaster) reader.readRaster(0, null);

                    return convertIccToRgb(raster, iccProfile);
                }
                catch (Exception e)
                {
                    return null;
                }
                finally
                {
                    IOUtils.closeQuietly(imageInputStream);
                }
            }
            else
            {
                JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new ByteArrayInputStream(image));
                return decoder.decodeAsBufferedImage();
            }
        }
        else
        {
            throw new IllegalArgumentException("Not a jpeg image");
        }
    }

出于某些原因,我想尽可能少地修改上面的代码。理想情况下,我只想用对我的新类的调用替换对 Sanselan 的调用:

BufferedImage toBufferedImageJpeg2(byte[] image) throws Exception
    {
        ImageMetaInfo imageInfo = new ImageMetaInfo(image);
        if (isImageTooBig(imageInfo.getHeight(), imageInfo.getWidth())) // Sanselan
        {
            throw new IllegalArgumentException("Image is too big");
        }
        if (imageInfo.getFormat().equals(ImageFormat.IMAGE_FORMAT_JPEG)) // Sanselan
        {
            if (ImageInfo.COLOR_TYPE_CMYK == imageInfo.getColorType()) // Sanselan
            {
                ICC_Profile iccProfile = imageInfo.getICCProfile(); // Sanselan
                ImageInputStream imageInputStream = ImageIO.createImageInputStream(new ByteArrayInputStream(image));

我的方法是创建一个对象,该对象提供与我们当前调用的方法相同的方法(getHeightgetWidthgetColorTypegetIccProfilegetFormat)。我会将byte [] 传递给对象构造函数,并定义两个策略(不确定是否应该将它们称为策略)。主要策略将尝试使用 Sanselan 读取图像。如果抛出异常,故障转移策略将尝试使用新库 Metadata Extractor 读取图像。

public class ImageMetaInfo
{

    public static final String IMAGE_FORMAT_JPEG = "JPEG";

    private static Log LOGGER = LogFactory.getLog(ImageValidator.class);

    private final int width;
    private final int height;
    private final String format;
    private final int colorType;
    private final ICC_Profile icc_profile;

    public ImageMetaInfo(byte[] image) throws Exception
    {
        ImageMetaInfoWrapper wrapper;
        try
        {
            wrapper = new SanselanImageMetaInfoWrapper();
        }
        catch (ImageReadException | IOException e)
        {
            LOGGER.error("Image could not be read with Sanselan, trying Metadata Extractor");
            try
            {
                wrapper = new MetadataExtractorMetaInfoWrapper();
            }
            catch (ImageProcessingException | IOException | MetadataException e1)
            {
                LOGGER.error("Also failed to read the image using Metadata Extractor", e1);
                throw new Exception("Couldn't create instance from the given data", e1);
            }
        }

        this.width = wrapper.getWidth();
        this.height = wrapper.getHeight();
        this.format = wrapper.getFormat();
        this.icc_profile = wrapper.getICCProfile();
        this.colorType = wrapper.getColorType();
    }

    public int getWidth()
    {
        return width;
    }

    public int getHeight()
    {
        return height;
    }

    public Object getFormat()
    {
        return format;
    }

    public int getColorType()
    {
        return colorType;
    }

    public ICC_Profile getICCProfile()
    {
        return icc_profile;
    }

}

然后,在ImageMetaInfoWrapper 的每个实现中,使用相应的库解析图像。 我的问题是这看起来真的太复杂了。我在另一个包装器 (ImageMetaInfo) 中有一个包装器 (ImageMetaInfoWrapper)。

  1. 对此是否有更好/更简洁的方法,将现有代码的更改保持在最低限度?
  2. 可以/应该在这里使用Strategy Pattern 吗?

我曾考虑在 Code Review 中发布这个问题,但我认为我的问题是更高层次的,更多的是关于设计编码。

【问题讨论】:

    标签: java refactoring decoupling


    【解决方案1】:

    另一种选择是使用模板方法模式。将与 Sanselan 相关的功能或替代品推送到子类中

    如果您的实用程序方法是其类的主要用途,您可以为不同的库创建它的子类。 如果这是一个小问题,您可能需要先提取方法对象。

    当然,您最终可能还需要一个工厂来为您提供正确的子类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-21
      • 2016-02-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多