Personal understanding of the bridge pattern, and its combined use with the factory method pattern and the builder pattern

After studying design patterns for a while, I want to share my understanding. I welcome your advice and point out the shortcomings.

Bridge mode: take the store and mobile phone as an example to describe, let's start with simple dependencies


public interface Phone {
	/** 充电 **/
	void charge();

	/** 解锁 **/
	void unlock();
}

public class ApplePhone implements Phone {
	public void charge() {
		System.out.println("普通充电");
	}

	public void unlock() {
		System.out.println("指纹解锁");
	}
}

public class SonyPhone implements Phone {
	public void charge() {
		System.out.println("快充");
	}

	public void unlock() {
		System.out.println("面部解锁");
	}
}

public class Store {
	/**
	 * 补充手机
	 */
	public void supplyPhone() {
		Phone phone = new SonyPhone();
		// 补充手机逻辑...
		System.out.println("补充手机完成");
	}
}

The above diagram is very simple, that is, there are two implementations of the Phone interface. Store only relies on the Phone interface and does not depend on the specific implementation, which makes it easier for the Store to change phones.

If now, want to add an implementation, NokiaPhone, and have the following requirements:

1. The charging method of NokiaPhone should be the same as that of SonyPhone, which is also fast charging.

2. The unlock method of NokiaPhone should be the same as that of ApplePhone, which is also fingerprint unlocking.

Seeing the first point, people don't hesitate to think that NokiaPhone can inherit SonyPhone.

Then I saw 2 o'clock, and I found the problem caused by NokiaPhone inheriting SonyPhone. NokiaPhone cannot reuse fingerprint unlocking by inheriting ApplePhone (cannot inherit multiple classes), and can only copy ApplePhone forcibly. as follows

public class NokiaPhone extends SonyPhone {
	public void unlock() {
		System.out.println("指纹解锁");
	}
}

It can be seen that inheritance will also cause the problem that code cannot be reused

In addition, if now NokiaPhone wants to add a function of taking pictures (takePicture).

Since the Store relies on the Phone, the Phone has to add the takePicture method

public interface Phone {
	/** 充电 **/
	void charge();

	/** 解锁 **/
	void unlock();

	/** 照相 **/
	void takePicture();
}

public class ApplePhone implements Phone {
	public void charge() {
		System.out.println("普通充电");
	}

	public void unlock() {
		System.out.println("指纹解锁");
	}

	public void takePicture() {
		// 空实现
		return;
	}
}

public class SonyPhone implements Phone {
	public void charge() {
		System.out.println("快充");
	}

	public void unlock() {
		System.out.println("面部解锁");
	}

	public void takePicture() {
		// 空实现
		return;
	}
}

public class NokiaPhone extends SonyPhone {
	public void unlock() {
		System.out.println("指纹解锁");
	}

	public void takePicture() {
		System.out.println("双摄");
	}
}
From the code point of view, once one of the implementations adds a unique function, the other implementations have to be modified, adding a lot of empty implementation code.

Summary: Inheritance cannot solve code reuse very well, and it is not easy to expand.

Looking back at the original implementation methods of SonyPhone and ApplePhone, we can classify

charge: normal charge, fast charge

unlock: fingerprint unlock, face unlock

The newly added NokiaPhone is equivalent to extracting the required implementation from each of the above functions to combine.


Does it feel like this, if you put normal charging, fast charging, fingerprint unlocking, and face unlocking into different categories, and then just set it to the NokiaPhone as needed.

In fact, the bridge mode is similar to this idea.


You should find that this type of diagram is different from what you often see. Abstraction lacks a subclass RefineAbstraction. Since this Xiaobai has been unable to figure out the function of RefineAbstraction, I also want to pass this article, and hope that all the great gods can draw their swords to help.

Draw the corresponding class diagram according to this example


public interface Locker {
	void unlock();
}

public class FaceLocker implements Locker {
	public void unlock() {
		System.out.println("指纹解锁");
	}
}

public class FingerprintLocker implements Locker {
	public void unlock() {
		System.out.println("面部解锁");
	}
}

public interface Charger {
	void charge();
}

public class QuickCharger implements Charger {
	public void charge() {
		System.out.println("普通充电");
	}
}

public class SimpleCharger implements Charger {
	public void charge() {
		System.out.println("快充");
	}
}

public class Phone {
	private Charger charger;
	private Locker locker;

	public void charge() {
		if (charger == null) {
			return;
		}

		charger.charge();
	}

	public void unlock() {
		if (locker == null) {
			return;
		}

		locker.unlock();
	}

	public void setCharger(Charger charger) {
		this.charger = charger;
	}

	public void setLocker(Locker locker) {
		this.locker = locker;
	}
}

public class Store {
	/**
	 * 补充手机
	 */
	public void supplyPhone() {
		Phone phone = getNokiaPhone();
		// 补充手机逻辑...
		System.out.println("补充手机完成");
	}
	
	private Phone getSonyPhone(){
		Phone phone = new Phone();
		phone.setCharger(new QuickCharger());
		phone.setLocker(new FaceLocker());
		return phone;
	}
	
	private Phone getApplePhone(){
		Phone phone = new Phone();
		phone.setCharger(new SimpleCharger());
		phone.setLocker(new FingerprintLocker());
		return phone;
	}
	
	private Phone getNokiaPhone(){
		Phone phone = new Phone();
		phone.setCharger(new QuickCharger());
		phone.setLocker(new FingerprintLocker());
		return phone;
	}
}


With the bridge mode, the Store can freely combine the types of mobile phones it needs.

You may ask, if the user-type Store is replaced with other types of mobile phones, the code must be modified, which is unscientific.

This problem involves how to obtain the instance object  problem, the bridge mode only solvesHow to deal with instance object dependencies  . (It will be mentioned later how to combine the factory method mode and the builder mode to facilitate the acquisition of different mobile phones)

At this time, if you add a camera function (takePicture)

public interface Camera {
	void takePicture();
}

public class DoubleCamera implements Camera {
	public void takePicture() {
		System.out.println("双摄");
	}
}

///
public class Phone {
	private Charger charger;
	private Locker locker;
	private Camera camera;

	public void charge() {
		if (this.charger == null) {
			return;
		}

		this.charger.charge();
	}

	public void unlock() {
		if (locker == null) {
			return;
		}

		this.locker.unlock();
	}

	public void takePicture() {
		if (this.camera == null) {
			return;
		}
		this.camera.takePicture();
	}

	public void setCharger(Charger charger) {
		this.charger = charger;
	}

	public void setLocker(Locker locker) {
		this.locker = locker;
	}

	public void setCamera(Camera camera) {
		this.camera = camera;
	}
}
///
public class Store {
	/**
	 * 补充手机
	 */
	public void supplyPhone() {
		Phone phone = getNokiaPhone();
		// 补充手机逻辑...
		System.out.println("补充手机完成");
	}
	
	private Phone getSonyPhone(){
		Phone phone = new Phone();
		phone.setCharger(new QuickCharger());
		phone.setLocker(new FaceLocker());
		return phone;
	}
	
	private Phone getApplePhone(){
		Phone phone = new Phone();
		phone.setCharger(new SimpleCharger());
		phone.setLocker(new FingerprintLocker());
		return phone;
	}
	
	private Phone getNokiaPhone(){
		Phone phone = new Phone();
		phone.setCharger(new QuickCharger());
		phone.setLocker(new FingerprintLocker());
		phone.setCamera(new DoubleCamera());
		return phone;
	}
}
At this time, the Phone class only needs to add a takePicture method, and the Store class only modifies the getNokiaPhone method (because only Nokia needs to add the camera function), without affecting other methods.


Summary: If a type of product (Phone in this example) has multiple different implementations in multiple dimensions (there are multiple implementations of methods such as charge, camera, and takePicture). If you use inheritance at this time, there will be problems such as difficult code reuse and inconvenient addition of dimensions. It is suitable to use bridge mode at this time

Factory method mode - builder mode - bridge mode : finally reached the climax, please read  the personal understanding of the factory mode and builder mode, and the combination


public interface PhoneBuilder {
	void buildCharger();
	
	void buildLocker();

	Phone getResult();
}

public class ApplePhoneBuilder implements PhoneBuilder {
	private Phone phone = new Phone();

	public void buildCharger() {
		phone.setCharger(new SimpleCharger());
	}

	public void buildLocker() {
		phone.setLocker(new FingerprintLocker());
	}

	public Phone getResult() {
		return phone;
	}
}

public class SonyPhoneBuilder implements PhoneBuilder {
	private Phone phone = new Phone();

	public void buildCharger() {
		phone.setCharger(new QuickCharger());
	}

	public void buildLocker() {
		phone.setLocker(new FaceLocker());
	}

	public Phone getResult() {
		return phone;
	}
}
///
public class PhoneDirector {
	public Phone construct(PhoneBuilder builder) {
		builder.buildCharger();
		builder.buildLocker();
		return builder.getResult();
	}
}

public interface PhoneFactory {
	Phone getPhone();
}

public class ApplePhoneFactory implements PhoneFactory {
	private PhoneDirector director = new PhoneDirector();

	public Phone getPhone() {
		ApplePhoneBuilder builder = new ApplePhoneBuilder();
		return director.construct(builder);
	}
}

public class SonyPhoneFactory implements PhoneFactory {
	private PhoneDirector director = new PhoneDirector();

	public Phone getPhone() {
		SonyPhoneBuilder builder = new SonyPhoneBuilder();
		return director.construct(builder);
	}
}
///
public class Store {
	private PhoneFactory phoneFactory;

	public Store(PhoneFactory phoneFactory) {
		super();
		this.phoneFactory = phoneFactory;
	}

	/**
	 * 补充手机
	 */
	public void supplyPhone() {
		Phone phone = phoneFactory.getPhone();
		// 补充手机逻辑...
		System.out.println("补充" + phone.getBrand() + "手机完成");
	}

	public static void main(String[] args) {
		StoreB storeB = new StoreB(new SonyPhoneFactory());
		storeB.supplyPhone();
	}
}


Summarize:

The bridge mode extracts the multi-dimensional changes (charge and unlock methods have different implementations) of a class of products (mobile phones in this example) into separate classes (implementations of Charger, Locker, etc.).

The Builder role of the builder pattern is responsible for selecting the required implementation classes from each dimension

The factory class, in the end, only needs to pass the corresponding Builder into the Director, and call the Director to start building in order.








Guess you like

Origin blog.csdn.net/MonkeyD5/article/details/73612791