适合任务的设计模式

时间:2009-01-24 07:53:38

标签: design-patterns oop

我需要以各种格式保存图像文件。 格式列表可能会经常更改,因此我希望保存可以非常可扩展。 此外,保存可以在许多地方进行(硬盘,ftp,http等)。保存位置的列表也会经常更改。

我以为我会使用基本的Image类和每种格式的许多派生类:

ImageBase {}
JpegImage : ImageBase {}
TiffImage : ImageBase{}

并适当地处理每个子类中的保存以进行格式化。 这是一个很好的设计决定吗?

另外,如何附加可扩展的保存位置机制(Ftp,文件共享等)?

我想要这样的事情:

var image=ImageBase.GetImageFromDisk(path);
//some casting to subclass maybe??
var tiffImage=image as TiffImage;
tiffImage.Location=new FtpLocation();//not sure if this is a good idea
tiffImage.Save();

这里的问题是,具体的图像实现不应该知道或关心保存位置。 在图像子类上调用Save();时,我想将工作委托给某个类,例如FtpLocation

请告知如何将各个部分放在一起。

谢谢。

的Valentin。

6 个答案:

答案 0 :(得分:5)

首先,我会在你的Image上实现流。这样,您可以从流中创建构造函数,以及为任何“图像”子类创建流的方法。

此外,我会创建您的“保存”基础架构,以简单地接受流并将其写入适当的技术(ftp,文件等)。

这样你最终得到可扩展的图像(如果你可以获得一个流,你可以做很多很多事情),你最终得到一个可扩展的保存基础设施(任何可以转到流的东西都可以被保存)

编辑:就个人而言,数据类型对象的保存方法听起来像是在错误的地方,但不知道整个系统我不能肯定地说。只是我的2c。

答案 1 :(得分:1)

在我看来,具体的课程应该只处理原始数据;这可能是写一个本地文件(然后由基本代码处理),或者是一个流。

例如,可能是:

public void Save()
{
    // TODO: add any language-specific constructs like "using", etc
    Stream stream = Location.OpenWrite();
    Save(stream);
    stream.Close();
}

protected abstract void Save(Stream stream);

因此Location负责提供流(可以是内存流,传输流,临时文件流等),并且可选择在关闭该流时执行额外的工作(通过封装内部流通过装饰器模式)。所有子类都写入流。

加载有点棘手,因为基类必须(假设来自基类的建议静态加载)偷看流以识别类型。但最终,你可以有类似的东西:

public static ImageBase Load(Location location)
{
    // TODO: add any language-specific constructs like "using", etc
    Stream stream = location.OpenRead();
    // TODO: wrap in a buffered/seekable stream so we can peek
    // TODO: parse headers and resolve image type
    ImageBase image = ...
    image.Location = location;
    stream.Position = 0; // rewind buffered/seekable stream
    // (don't use Load() since we have already opened the stream)
    image.Load(stream);
    stream.Close();
    return image;
}
protected abstract void Load(Stream stream);
public void Load()
{
    // TODO: add any language-specific constructs like "using", etc
    Stream stream = Location.OpenRead();
    Load(stream); // don't need to buffer if loading from subclass
    stream.Close();
}

答案 2 :(得分:1)

我的方式略有不同。 (语法是Java。)

public class Image {
    public void load(byte[] imageData);
    public byte[] getImageData();
}

public class JpegImage extends Image {
    public void load(byte[] imageData) {
        /* decode image data. */
    }

    public byte[] getImageData() {
        /* encode and return the JPG data. */
    }
}

public class Location {
    public Image loadImage(String uri);
    public void saveImage(Image image);
}

public class HttpLocation extends Location {
    public Image loadImage(String uri) {
        byte[] = getData(uri);
        if (type == JPEG) {
            return new JpegImage().load(byte);
        } else if (type == PNG) {
            return new PngImage().load(byte);
        }
    }
    public void saveImage(Image image) {
        byte[] imageData = image.getImageData();
        /* upload. */
    }
}

从Web服务器的内容类型到Image类的映射也可能在Location基类(或完全不同的帮助器类)中以更可重用的方式发生,但这就是我将如何进行的。

答案 3 :(得分:0)

在ImageBase类中实现Save(),而不是在派生类中实现。

答案 4 :(得分:0)

我认为将图像本身子类化为保存/加载不是一个正确的想法,因为你没有实例来调用Load on。内存中的图像也完全独立于原始格式 - 例如,打开jpg然后将其另存为png是绝对可能的。

我会按以下方式进行:

Image

ImageFormat { Save(Image, Stream); Image Load(Stream); }
JpegFormat : ImageFormat {}
TiffFormat : ImageFormat {}

现在,您只需提供一种获取阅读流和写作流的方法。

答案 5 :(得分:0)

对于图像,我会继承。对于加载/保存部分,我将使用策略设计模式,以便每个图像都可以具有LoadingAlgorithm和SavingAlgorithm。这样您就可以改变加载和保存过程,而不必进行Image类的加载和保存方法。