johnny li博客

构建只为纯粹书写的博客

设计模式:建造者模式

johnny 算法

介绍。◕ᴗ◕。

定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

作用

用户只需要知道建造的类型就可以得到他们,而无需知道具体的建造过程和细节

解决的问题

  • 方便用户创建复杂的对象(不需要知道实现过程)
  • 代码复用性、封装性(将对象构建过程和细节进行封装、复用

模式说明

1.UML类图

建造者模式

2.实例说明

  • 背景:用户输入不同的类型(1,2,3),输出不同的二维码分享图
  • 图例说明:如下图,穿插了两种产品(Product)—— 画直播间图(BaseDrawRoomImage)、画课程图(BaseDrawCourseImage)。两个都是继承自抽象接口AbstractDrawImage(提供画圆形、方形、字符串、计算字符串等通用方法)。 BaseDrawRoomImage(Builder)下有具体的DrawLiveRoomImage 1 2 两个实现类(ContractBuilder) BaseDrawCourseImage(Builder)下有具体的DrawCourseImage 1 2 两个实现类(ContractBuilder) DrawDetector类(Director)为指挥者 图例

springboot 源码

public abstract class AbstractDrawShareImage {

    protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractDrawShareImage.class);

    protected BufferedImage baseImage;
    protected Graphics2D g2d;

    /**
     * 画背景
     */
    public void drawBackgroudImage(String baseImagePathName) throws IOException {
        Resource resource = new ClassPathResource(baseImagePathName);
        this.baseImage = ImageIO.read(resource.getInputStream());
        this.g2d = this.baseImage.createGraphics();
    }


    /**
     * 画圆形
     *
     * @param imageUrl
     * @param x
     * @param y
     * @param maxLength
     */
    public void drawCircularImage(String imageUrl, int x, int y, int maxLength) {
        try {
            BufferedImage image = ImageIO.read(new URL(imageUrl));
            this.g2d.drawImage(ImageUtils.convertCircular(image, image.getWidth() > maxLength ? maxLength : image.getWidth()).getScaledInstance(maxLength, maxLength, Image.SCALE_SMOOTH), x, y, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 画矩形
     *
     * @param imageUrl
     * @param x
     * @param y
     * @param length
     * @param width
     */
    public void drawRectangle(String imageUrl, int x, int y, int length, int width) {
        BufferedImage image = null;
        try {
            image = ImageIO.read(new URL(imageUrl));
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.g2d.drawImage(image, x, y, width, length, null);
    }
    /**
     * 画字符串
     *
     * @param text
     * @param x
     * @param y
     * @param color
     * @param font
     */
    public void drawString(String text, int x, int y, Color color, Font font) {
        this.g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        this.g2d.setFont(font);
        this.g2d.setColor(color);
        this.g2d.drawString(text, x, y);
    }

    public int getFontWitch(Font font, int baseWidth, String text) {
        if (StringUtils.isEmpty(text)) {
            return 0;
        }
        // 计算文字长度,计算居中的x点坐标
        return (baseWidth - getWordWidth(font, text)) / 2;
    }

    public int getWordWidth(Font font, String word) {
        if (StringUtils.isEmpty(word)) {
            return 0;
        }
        FontMetrics fm = this.g2d.getFontMetrics(font);
        return fm.stringWidth(word);
    }

}

public abstract class BaseDrawRoomImage extends AbstractDrawShareImage {
    protected int baseWidth = 0;
    protected static final String TIME_TEXT = "邀请卡有效期至";

    public abstract void drawBackgroudImage() throws IOException;

    /**
     * 画头像
     *
     * @param headUrl
     */
    public abstract void drawHeadImage(String headUrl);
    /**
     * 昵称
     *
     * @param nick
     */
    public abstract void drawNick(String nick);
    /**
     * 画二维码
     *
     * @param qrcodeUrl
     */
    public abstract void drawQrCode(String qrcodeUrl);
    /**
     * 画直播间名称
     *
     * @param roomName
     */
    public abstract void drawLiveRoomName(String roomName);
    /**
     * 画二维码有效日期文字
     *
     * @param expireText
     */
    public abstract void drawDeadline(String expireText);
}


public class DrawLiveRoomImage1 extends BaseDrawRoomImage {
   //具体 override 省略...
}

//具体建造,画头像、二维码、昵称、背景、过期时间
@Component("DrawLearnImage")
public  class DrawDerecter {
    private AbstractDrawShareImage drawShareImage;
    public Image drawImage(String headUrl, String nick, String roomName, String qrcodeUrl, String expireText) throws CustomerException {
        if (drawShareImage instanceof BaseDrawRoomImage) {
            BaseDrawRoomImage baseDrawRoomImage1 = (BaseDrawRoomImage) drawShareImage;
            try {
                baseDrawRoomImage1.drawBackgroudImage();
            } catch (Exception e) {
                BusinessException.throwCustomException(BusinessException.DRAW_LEARN_IMAGE_FAILED, "image not fond");
            }

            baseDrawRoomImage1.drawHeadImage(headUrl);
            baseDrawRoomImage1.drawQrCode(qrcodeUrl);
            baseDrawRoomImage1.drawLiveRoomName(roomName);
            baseDrawRoomImage1.drawNick(nick);
            baseDrawRoomImage1.drawDeadline(expireText);
            baseDrawRoomImage1.g2d.dispose();

            drawShareImage.g2d.dispose();
            return baseDrawRoomImage1.baseImage;
        }

        BusinessException.throwCustomException(BusinessException.DRAW_LEARN_IMAGE_FAILED);
        return null;
    }
}

优缺点

优点

  1. 易于解耦 将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。
  2. 易于精确控制对象的创建将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
  3. 易于拓展 增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则”。 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。

缺点

  1. 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  2. 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

最佳实践

当创建复杂对象的算法独立于该对象的组成部分以及他们的装配方式时使用

johnny
构建只为纯粹书写的博客