設計模式-建造者模式

建造者模式

也叫生成器模式,他是一個創(chuàng)建型模式

通用類圖

image-20210103175659169

Product產(chǎn)品類

? 通常是實現(xiàn)了模板方法模式,也就是有模板方法和基本方法.

public class Product { 
  public void doSomething(){ 
    //獨立業(yè)務處理
  }
}

Builder抽象建造者

? 規(guī)范產(chǎn)品的組建,一般是由子類實現(xiàn)。例子中的CarBuilder就屬于 抽象建造者。

public abstract class Builder { 
  //設置產(chǎn)品的不同部分,以獲得不同的產(chǎn)品
  public abstract void setPart(); 
  //建造產(chǎn)品 
  public abstract Product buildProduct();
}

ConcreteBuilder具體建造者

? 實現(xiàn)抽象類定義的所有方法,并且返回一個組建好的對象。例子中 的BenzBuilder和BMWBuilder就屬于具體建造者。

public class ConcreteProduct extends Builder { 
  private Product product = new Product(); 
  //設置產(chǎn)品零件
  public void setPart(){

    /* * 產(chǎn)品類內(nèi)的邏輯處理 */ 
  } 
  //組建一個產(chǎn)品 
  public Product buildProduct() {

    return product; 
  }

}

Director導演類

? 負責安排已有模塊的順序,然后告訴Builder開始建造,在上面的例 子中就是我們的老大,××公司找到老大,說我要這個或那個類型的車輛 模型,然后老大就把命令傳遞給我,我和我的團隊就開始拼命地建造, 于是一個項目建設完畢了。

public class Director {
  private Builder builder = new ConcreteProduct();
  //構建不同的產(chǎn)品 
  public Product getAProduct(){
    builder.setPart();
    /* * 設置不同的零件,產(chǎn)生不同的產(chǎn)品 */
    return builder.buildProduct();
  }
}

定義

將一個復雜對象的構建和他的表示分離,使得同樣的構建過程可以創(chuàng)建不同的表示。

特征

用戶只需要指定需要建造的類型就可以獲得對象,建造過程及細節(jié)不需要了解

適用場景

一個對象有非常復雜的內(nèi)部結構(很多屬性)

我們可以把復雜對象的創(chuàng)建和使用分離

可以不用關心誰先誰后了

  • 相同的方法,不同的執(zhí)行順序,產(chǎn)生不同的事件結果時,可以采用建造者模式。
  • 多個部件或零件,都可以裝配到一個對象中,但是產(chǎn)生的運行結果又不相同時,則可以使用該模式。
  • 產(chǎn)品類非常復雜,或者產(chǎn)品類中的調(diào)用順序不同產(chǎn)生了不同的效 能,這個時候使用建造者模式非常合適。
  • 在對象創(chuàng)建過程中會使用到系統(tǒng)中的一些其他對象,這些對象在產(chǎn)品對象的創(chuàng)建過程中不易得到時,也可以采用建造者模式封裝該對象的創(chuàng)建過程。該種場景只能是一個補償方法,因為一個對象不容易獲得,而在設計階段竟然沒有發(fā)覺,而要通過創(chuàng)建者模式柔化創(chuàng)建過程, 本身已經(jīng)違反設計的最初目標。

案例

  • JPA
  • mybatis example

案例類圖

image-20210103175337982

當前執(zhí)行的順序由具體的構建者來決定,用戶只需要關心需要的內(nèi)容

  • 如果你要調(diào)用類中的成員變量或方法,需要在前面加上this關鍵字,不加也能正常地跑起來,但是不清晰,加上this關鍵 字,我就是要調(diào)用本類中的成員變量或方法,而不是本方法中的一個變 量。
  • 還有super方法也是一樣,是調(diào)用父類的成員變量或者方法,那就加上這個關鍵字,不要省略,這要靠約束,還有就是程序員的自覺性,他要是死不悔改,那咱也沒招。

源碼中的提現(xiàn)

StringBuilder

public AbstractStringBuilder append(String str) {
  if (str == null)
    return appendNull();
  int len = str.length();
  ensureCapacityInternal(count + len);
  str.getChars(0, len, value, count);
  count += len;
  return this;
}

@Override
public StringBuilder append(char c) {
  super.append(c);
  return this;
}
  • 過程開放給用戶
  • 結果程序來幫我們控制順序

Mybatis CacheBuilder

public CacheBuilder addDecorator(Class<? extends Cache> decorator) {
  if (decorator != null) {
    this.decorators.add(decorator);
  }
  return this;
}
public Cache build() {
  setDefaultImplementations();
  Cache cache = newBaseCacheInstance(implementation, id);
  setCacheProperties(cache);
  // issue #352, do not apply decorators to custom caches
  if (PerpetualCache.class.equals(cache.getClass())) {
    for (Class<? extends Cache> decorator : decorators) {
      cache = newCacheDecoratorInstance(decorator, cache);
      setCacheProperties(cache);
    }
    cache = setStandardDecorators(cache);
  } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
    cache = new LoggingCache(cache);
  }
  return cache;
}

Mybatis SqlsessionFactoryBuilder

public SqlSessionFactory build(Reader reader, String environment) {
  return build(reader, environment, null);
}

public SqlSessionFactory build(Reader reader, Properties properties) {
  return build(reader, null, properties);
}

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      reader.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

Spring beanDefinitionBuilder

public AbstractBeanDefinition getBeanDefinition() {
  this.beanDefinition.validate();
  return this.beanDefinition;
}
public static BeanDefinitionBuilder genericBeanDefinition(String beanClassName) {
  BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
  builder.beanDefinition.setBeanClassName(beanClassName);
  return builder;
}


public static BeanDefinitionBuilder genericBeanDefinition(Class<?> beanClass) {
  BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
  builder.beanDefinition.setBeanClass(beanClass);
  return builder;
}

總結

優(yōu)點

  • 封裝性好,創(chuàng)建和使用分離
  • 擴展性好,建造類之間相互獨立,容易擴展
  • 便于控制細節(jié)風險
    • 由于具體的建造者是獨立的,因此可以對建造過程逐步細化,而不對其他模塊產(chǎn)生任何影響

缺點

  • 產(chǎn)生多余的Builder對象
  • 產(chǎn)品內(nèi)部發(fā)生變化,建造者都要修改,成本較大

和別的設計模式的區(qū)別

和工廠模式的區(qū)別

  • 建造者更加關心方法的調(diào)用順序,工廠模式注重于創(chuàng)建對象
    • 建造者模式關注的是零件類型和裝配工藝(順序),這是它與工廠方法模式最大不同的地方,雖然同為創(chuàng)建類模式,但是注重點不同。
  • 創(chuàng)建對象的力度不同,建造者模式創(chuàng)建復雜的對象,由各種復雜的部件組成,工廠模式創(chuàng)建出來的都一樣
  • 關注點不一樣
    • 工廠模式只要把對象創(chuàng)建出來就行了
    • 建造者不但需要創(chuàng)建,還需要了解這個對象由什么部件組成
  • 建造者模式根據(jù)建造過程的順序不一樣,最終的對象部件組成也不一樣

問題

什么情況下應該考慮到使用建造模式?

  • 相同的方法,不同的執(zhí)行順序會產(chǎn)生不同的結果的時候需要用到
  • 產(chǎn)品類非常復雜,或者產(chǎn)品類的調(diào)用順序不同產(chǎn)生了不同的效能
  • 對象穿件的過程會用到一些其他對象,對象在創(chuàng)建的過程中不容易得到

我的筆記倉庫地址gitee 快來給我點個Star吧

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容