将客户端代码与库分离

时间:2015-03-12 17:26:02

标签: java refactoring decoupling

我们有一个与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));

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

byte []

然后,在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)中有一个包装器(ImageMetaInfoWrapper)。

  1. 是否有更好/更清洁的方法,将现有代码的更改保持在最低限度?
  2. 可以/应该在Strategy Pattern使用吗?
  3. 我考虑在Code Review中发布问题,但我认为我的问题是更高层次,更多是关于编码的设计。

1 个答案:

答案 0 :(得分:0)

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

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

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