Andrwyw home archives categories

原文:http://javadesign-patterns.blogspot.com/p/factory-desig.html

# 工厂模式: 工厂方法模式是面向对象创建型的一种设计模式.工厂模式在创建对象时可不指定此对象的类别.

对象的创建通常都是一个复杂的过程,这些过程不适合放在一个对象中.对象的创建经常会导致大量的重复代码,并且不能提供足够的抽象.程序中,如果有关对象创建的代码到处都是,那么当你需要更改对象创建的过程时,在每一处你都需要做相应的改动.

为了解决这些问题,工厂模式定义一个单独的函数来创建对象.

简单来说,如果我们有一系列相似的类,并且希望为这些类创建对象,我们就可以使用工厂模式.比如,我们有父类和它的很多子类,根据提供的数据,我们需要获得相应子类的对象.

此模式让类与类之间的耦合变得很弱,这是设计程序时非常重要的一个原则.使用抽象实体而不是具体实现更能达到低耦合的效果.

何时使用:工厂模式可以用在下面几种情况中: 1. 一个类不知道它必须要创建的类. 2. 父类让子类决定何种对象会被创建. 3. 运行时,你需要根据输入参数创建对象.

如何使用:使用工厂模式,我们需要定义一个接口来创建对象,但是要让实现了此接口的类来决定哪个类会被实例化.

下面我们通过一个有问题的例子来更好地理解它.

假如你想开发一个媒体播放器,它可以播放mp3,mp4格式的音频视频文件.这个播放器的类图如下:

涉及到的接口和类如下所示

接口Decoder.java是这样写的

public interface Decoder {
	InputStream decodeFile(String mediaFileName);
}

MP3Decoder.java这样写

public class MP3Decoder implements Decoder {

   @Override
    public InputStream decodeFile(String mediaFileName) {
		//decode your file here and return the decoded stream
		System.out.println("MP3Decoder: decoding file "+mediaFileName);
		return null;
    }
}

MP4Decoder.java这样写

public class MP4Decoder implements Decoder {

@Override
public InputStream decodeFile(String mediaFileName) {
	//decode your file here and return the decoded stream
	System.out.println("MP4Decoder: decoding file "+mediaFileName);
	return null;
} }

Player.java是这样的

public class Player {

	public void init() {
		// initialize the various audio,video components here
	}

	public void play(String mediaFileName) {
        if (mediaFileName == null || "".equals(mediaFileName)) {
                   throw new RuntimeException("Invalid media file.");
        }
        Decoder decoder = getMediaDecoder(mediaFileName);
        if (decoder != null) {
                 play(decoder.decodeFile(mediaFileName));
        } else {
                 throw new RuntimeException("Media format not supported.");
        }
	}

	private Decoder getMediaDecoder(String mediaFileName) {
		mediaFileName = mediaFileName.toUpperCase();
		if (mediaFileName.endsWith(".MP3")) {
		          return new MP3Decoder();
		} else if (mediaFileName.endsWith(".MP4")) {
		          return new MP4Decoder();
		}
		return null;
	}

	private void play(InputStream stream) {
		System.out.println("Playing..$..$");
	}
}

问题:这里你可以看到,MP3Decoder/MP4Decoder都和Player紧密地关联着.如果Player类需要支持一种新的格式,那么就需要在Player类中新增一种decoder.所以每次我们都要修改Player类.

解决:为了避免这样的问题,我们可以使用工厂模式.使用了工厂模式的类图如下所示.

为了使用工厂模式,我们可以新引入一个DecoderFactory.java类,并且把Decoder对象创建从Player类移动到这个类中.

public class DecoderFactory{

	public static Decoder getMediaDecoder(String mediaFileName) {
		mediaFileName = mediaFileName.toUpperCase();
		if (mediaFileName.endsWith(".MP3")) {
			return new MP3Decoder();
		} else if (mediaFileName.endsWith(".MP4")) {
			return new MP4Decoder();
		}
		return null;
	}
}

现在修改Player类来使用工厂类.

public class Player {
	public void init() {
	       // initialize the various audio,video components here
	}

	public void play(String mediaFileName) {
		if (mediaFileName == null || "".equals(mediaFileName)) {
	        throw new RuntimeException("Invalid media file.");
		}
		Decoder decoder = DecoderFactory.getMediaDecoder(mediaFileName);
		if (decoder != null) {
			play(decoder.decodeFile(mediaFileName));
		} else {
			throw new RuntimeException("Media format not supported.");
		}
	}

	private void play(InputStream stream) {
		System.out.println("Playing..$..$");
	}
}

为了测试player的工厂类,我们可以写一个FactorPatternDemo.java类,如下.

public class FactorPatternDemo {

	public static void main(String[] args) {
		String mediaFile = "C://songs/song1.mp3";
		Player player = new Player();
		player.init();
		player.play(mediaFile);
		//play other file
		mediaFile = "C://videos/song2.mp4";
		player.play(mediaFile);
	}
}

Output:

MP3Decoder: decoding file C://songs/song1.mp3 Playing..$..$ MP4Decoder: decoding file C://videos/song2.mp4 Playing..$..$

现在再看Player类,与Decoder之间的关联已经被DecoderFactor移除了.如果Player需要支持一种新的格式,我们也不需要修改player类了,只有Factory需要为新的Decoder进行修改.

p.s.

需要注意