国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Programming DSL: JMatchers

Yuanf / 2472人閱讀

摘要:袁英杰回顧設(shè)計(jì)上次在軟件匠藝小組上分享了正交設(shè)計(jì)的基本理論,原則和應(yīng)用,在活動(dòng)線下收到了很多朋友的反饋。強(qiáng)迫用戶雖然的設(shè)計(jì)高度可復(fù)用性,可由用戶根據(jù)實(shí)際情況,自由拼裝組合各種算子。鳴謝正交設(shè)計(jì)的理論原則及其方法論出自前軟件大師袁英杰先生。

軟件設(shè)計(jì)是一個(gè)「守破離」的過程。 --袁英杰

回顧設(shè)計(jì)

上次在「軟件匠藝小組」上分享了「正交設(shè)計(jì)」的基本理論,原則和應(yīng)用,在活動(dòng)線下收到了很多朋友的反饋。其中有人談及到了DSL的設(shè)計(jì),為此我將繼續(xù)以find為例,通過「正交設(shè)計(jì)」的應(yīng)用,重點(diǎn)討論DSL的設(shè)計(jì)過程。

首先回顧一下之前find算子重構(gòu)的成果。

public  Optional find(Iterable c, Predicate p) {
  for (E e : c)
    if (p.test(e))
      return Optional.of(e);
  return Optional.empty();
}

另外根據(jù)需求1~4,抽象了2個(gè)變化方向:

比較運(yùn)算:==, !=

邏輯運(yùn)算:&&, ||

比較語(yǔ)義
public interface Matcher {
  boolean matches(T actual);
    
  static  Matcher eq(T expected) {
    return actual -> expected.equals(actual);
  }
  
  static  Matcher ne(T expected) {
    return actual -> !expected.equals(actual);
  }
}

查找年齡不等于18歲的學(xué)生,可以如此描述。

assertThat(find(students, age(ne(18))).isPresent(), is(true));
邏輯語(yǔ)義
public interface Predicate {
  boolean test(E e);

  default Predicate and(Predicate other) {
    return e -> test(e) && other.test(e);
  }
  
  default Predicate or(Predicate other) {
    return e -> test(e) || other.test(e);
  }
}

查找名字叫horance的男生,可以表述為:

assertThat(find(students, name(eq("horance")).and(Human::male)).isPresent(), is(true));
探索前進(jìn)

接下來繼續(xù)通過需求的演進(jìn)和迭代,完善既有的設(shè)計(jì)和實(shí)現(xiàn)。應(yīng)用「正交設(shè)計(jì)」的基本原則,加深對(duì)DSL設(shè)計(jì)的理解。

引入工廠
public interface Matcher {
  boolean matches(T actual);
    
  static  Matcher eq(T expected) {
    return actual -> expected.equals(actual);
  }
  
  static  Matcher ne(T expected) {
    return actual -> !expected.equals(actual);
  }
}

將所有的Static Factory方法都放在接口中,雖然簡(jiǎn)單,也很自然。但如果方法之間產(chǎn)生重復(fù)代碼,需要「提取函數(shù)」,設(shè)計(jì)將變得非常不靈活,因?yàn)榻涌趦?nèi)所有方法都將默認(rèn)為public,這往往不是我們所期望的,為此可以將這些Static Factory方法搬遷到Matchers實(shí)用類中去。

public final class Matchers {    
  public static  Matcher eq(T expected) {
    return actual -> expected.equals(actual);
  }
  
  public static  Matcher ne(T expected) {
    return actual -> !expected.equals(actual);
  }
  
  private Matchers() {
  }
}
實(shí)現(xiàn)大于

需求5: 查找年齡大于18歲的學(xué)生

assertThat(find(students, age(gt(18)).isPresent(), is(true));
public final class Matchers {
  ......
  
  public static > Matcher gt(T expected) {
    return actual -> Ordering.natural().compare(actual, expected) > 0;
  }
}

其中,natural代表了一種自然的比較規(guī)則。

public final class Ordering {
  public static > Comparator natural() {
    return (t1, t2) -> t1.compareTo(t2);
  }
}
實(shí)現(xiàn)小于

需求6: 查找年齡小于18歲的學(xué)生

assertThat(find(students, age(lt(18)).isPresent(), is(true));

依次類推,「小于」的規(guī)則實(shí)現(xiàn)如下:

public final class Matchers {
  ......
  
  public static > Matcher gt(T expected) {
    return actual -> Ordering.natural().compare(actual, expected) > 0;
  }
  
  public static > Matcher lt(T expected) {
    return actual -> Ordering.natural().compare(actual, expected) < 0;
  }
}
提取函數(shù)

設(shè)計(jì)產(chǎn)生了明顯的重復(fù),可以通過「提取函數(shù)」來消除重復(fù)。

public final class Matchers {
  ......
  
  public static > Matcher gt(T expected) {
    return actual -> compare(actual, expected) > 0;
  }
  
  public static > Matcher lt(T expected) {
    return actual -> compare(actual, expected) < 0;
  }
  
  private static > int compare(T actual, T expected) {
    return Ordering.natural().compare(actual, expected);
  }
}

其余比較操作,例如大于等于,小于等于的設(shè)計(jì)和實(shí)現(xiàn)依此類推,在此不再重述。

包含子串

需求7: 查找名字中包含horance的學(xué)生

assertThat(find(students, name(contains("horance")).isPresent(), is(true));
public final class Matchers {    
  ......
  
  public static Matcher contains(String substr) {
    return str -> str.contains(substr);
  }
}
子串開頭

需求8: 查找名字以horance開頭的學(xué)生

assertThat(find(students, name(starts("horance")).isPresent(), is(true));
public final class Matchers {    
  ......
  
  public static Matcher starts(String substr) {
    return str -> str.startsWith(substr);
  }
}

「子串結(jié)尾」的邏輯,可以設(shè)計(jì)ends的關(guān)鍵字,實(shí)現(xiàn)依此類推,在此不再重述。

不區(qū)分大小寫

需求9: 查找名字以horance開頭,但不區(qū)分大小寫的學(xué)生

assertThat(find(students, name(starts_ignoring_case("horance")).isPresent(), is(true));
public final class Matchers {    
  ......

  public static Matcher starts(String substr) {
    return str -> str.startsWith(substr);
  }
  
  public static Matcher starts_ignoring_case(String substr) {
    return str -> lower(str).startsWith(lower(substr));
  }

  private static String lower(String s) {
    return s.toLowerCase();
  }
}

startsstarts_ignoring_case之間存在微妙的重復(fù)設(shè)計(jì),為此需要進(jìn)一步消除重復(fù)。

組合式設(shè)計(jì)
assertThat(find(students, name(ignoring_case(Matchers::starts, "Horance"))).isPresent(), is(true));

運(yùn)用函數(shù)的「組合式設(shè)計(jì)」,達(dá)到代碼的最大可復(fù)用性。從OO的角度看,ignoring_case是對(duì)starts, ends, contains的功能增強(qiáng),是一種典型的「修飾」關(guān)系。

public static Matcher ignoring_case(
  Function> m, String substr) {
  return str -> m.apply(lower(substr)).matches(lower(str));
}

其中,Function>是一個(gè)一元函數(shù),參數(shù)為String,返回值為Matcher

@FunctionalInterface
public interface Function {
    R apply(T t);
}
強(qiáng)迫用戶

雖然ignoring_case的設(shè)計(jì)高度可復(fù)用性,可由用戶根據(jù)實(shí)際情況,自由拼裝組合各種算子。但「方法引用」的語(yǔ)法,給用戶給造成了不必要的負(fù)擔(dān)。

assertThat(find(students, name(ignoring_case(Matchers::starts, "Horance"))).isPresent(), is(true));

可以提供starts_ignoring_case的語(yǔ)法糖,將用戶犯錯(cuò)的幾率降至最低,但要保證實(shí)現(xiàn)不存在重復(fù)設(shè)計(jì)。

assertThat(find(students, name(starts_ignoring_case("Horance"))).isPresent(), is(true));

此時(shí),ignoring_case也應(yīng)該重構(gòu)為private,變?yōu)橐粋€(gè)「可重用」的函數(shù)。

public static Matcher starts_ignoring_case(String substr) {
  return ignoring_case(Matchers::starts, substr);
}
 
private static Matcher ignoring_case(
  Function> m, String substr) {
  return str -> m.apply(lower(substr)).matches(lower(str));
}
修飾語(yǔ)義

需求13: 查找名字中不包含horance的第一個(gè)學(xué)生

assertThat(find(students, name(not_contains("horance")).isPresent(), is(true));
public final class Matchers {    
  ......
  
  public static Matcher not_contains(String substr) {
    return str -> !str.contains(substr);
  }
}

在這之前,也曾遇到過類似的「反義」的操作。例如,查找年齡不等于18歲的學(xué)生,可以如此描述。

assertThat(find(students, age(ne(18))).isPresent(), is(true));
public final class Matchers {    
  ......
  
  public static  Matcher ne(T expected) {
    return actual -> !expected.equals(actual);
  }
}

兩者對(duì)「反義」的描述存在兩份不同的表示,是一種隱晦的「重復(fù)設(shè)計(jì)」,需要一種巧妙的設(shè)計(jì)消除重復(fù)。

提取反義

為此,應(yīng)該刪除not_contains, ne的關(guān)鍵字,并提供統(tǒng)一的not關(guān)鍵字。

assertThat(find(students, name(not(contains("horance")))).isPresent(), is(true));

not的實(shí)現(xiàn)是一種「修飾」的手法,對(duì)既有的Matcher功能的增強(qiáng),巧妙地取得了「反義」功能。

public final class Matchers {    
  ......
  
  public static  Matcher not(Matcher matcher) {
    return actual -> !matcher.matches(actual);
  }
}
語(yǔ)法糖

對(duì)于not(eq(18))可以設(shè)計(jì)類似于not(18)的語(yǔ)法糖,使其更加簡(jiǎn)單。

assertThat(find(students, age(not(18))).isPresent(), is(true));

其實(shí)現(xiàn)就是對(duì)eq的一種修飾操作。

public final class Matchers {    
  ......
  
  public static  Matcher not(T expected) {
    return not(eq(expected));
  }
}
邏輯或

需求13: 查找名字中包含horance,或者以liu結(jié)尾的學(xué)生

assertThat(find(students, name(anyof(contains("horance"), ends("liu")))).isPresent(), is(true));
public final class Matchers {    
  ......
  
  @SafeVarargs
  public static  Matcher anyof(Matcher... matchers) {
    return actual -> {
      for (Matcher matcher : matchers)
        if (matcher.matches(actual)) 
          return true;
      return false;
    };
  }
}
邏輯與

需求14: 查找名字中以horance開頭,并且以liu結(jié)尾的學(xué)生

assertThat(find(students, name(allof(starts("horance"), ends("liu")))).isPresent(), is(true));
public final class Matchers {    
  ......
  
  @SafeVarargs
  public static  Matcher allof(Matcher... matchers) {
    return actual -> {
      for (Matcher matcher : matchers)
        if (!matcher.matches(actual))
          return false;
      return true;
    };
  }
}
短路

allofanyof之間的實(shí)現(xiàn)存在重復(fù)設(shè)計(jì),可以通過提取函數(shù)消除重復(fù)。

public final class Matchers {    
  ......
  
  @SafeVarargs
  private static  Matcher combine(
    boolean shortcut, Matcher... matchers) {
    return actual -> {
      for (Matcher matcher : matchers)
        if (matcher.matches(actual) == shortcut)
          return shortcut;
      return !shortcut;
    };
  }
  
  @SafeVarargs
  public static  Matcher allof(Matcher... matchers) {
    return combine(false, matchers);
  }
    
  @SafeVarargs
  public static  Matcher anyof(Matcher... matchers) {
    return combine(true, matchers);
  }
}
占位符

需求15: 查找算法始終失敗或成功

assertThat(find(students, age(always(false))).isPresent(), is(false));
public final class Matchers {    
  ......
  
  public static  Matcher always(boolean bool) {
    return e -> bool;
  }
}
回顧

通過15個(gè)需求的迭代和演進(jìn),通過運(yùn)用「正交設(shè)計(jì)」和「組合式設(shè)計(jì)」的基本思想,得到了一套接口豐富、表達(dá)力極強(qiáng)的DSL

這一套簡(jiǎn)單的DSL是一個(gè)高度可復(fù)用的Matcher集合,其設(shè)計(jì)既包含了OO的方法論,也涉及到了FP的思維,整體性設(shè)計(jì)保持高度的一致性和統(tǒng)一性。

鳴謝

「正交設(shè)計(jì)」的理論、原則、及其方法論出自前ThoughtWorks軟件大師「袁英杰」先生。英杰既是我的老師,也是我的摯友;其高深莫測(cè)的軟件設(shè)計(jì)的修為,及其對(duì)軟件設(shè)計(jì)獨(dú)特的哲學(xué)思維方式,是我等后輩學(xué)習(xí)的楷模。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65593.html

相關(guān)文章

  • Programming DSL:JSpec

    摘要:命名模式為了做到自動(dòng)發(fā)現(xiàn)機(jī)制,在運(yùn)行時(shí)完成用例的組織,規(guī)定所有的測(cè)試用例必須遵循的函數(shù)原型。在后文介紹,可以將理解為及其的運(yùn)行時(shí)行為其中,對(duì)于于子句,對(duì)于于子句。將的執(zhí)行序列行為固化。 There are two ways of constructing a software design. One way is to make it so simple that there are ...

    TANKING 評(píng)論0 收藏0
  • 【jOOQ中文】2. jOOQ與Spring和Druid整合

    摘要:在這個(gè)例子中,我們將整合但您也可以使用其他連接池,如,,等。作為構(gòu)建和執(zhí)行。 jOOQ和Spring很容易整合。 在這個(gè)例子中,我們將整合: Alibaba Druid(但您也可以使用其他連接池,如BoneCP,C3P0,DBCP等)。 Spring TX作為事物管理library。 jOOQ作為SQL構(gòu)建和執(zhí)行l(wèi)ibrary。 一、準(zhǔn)備數(shù)據(jù)庫(kù) DROP TABLE IF EXIS...

    pingink 評(píng)論0 收藏0
  • 手把手教你從零寫一個(gè)簡(jiǎn)單的 VUE--模板篇

    摘要:轉(zhuǎn)換成為模板函數(shù)聯(lián)系上一篇文章,其實(shí)模板函數(shù)的構(gòu)造都大同小異,基本是都是通過拼接函數(shù)字符串,然后通過對(duì)象轉(zhuǎn)換成一個(gè)函數(shù),變成一個(gè)函數(shù)之后,只要傳入對(duì)應(yīng)的數(shù)據(jù),函數(shù)就會(huì)返回一個(gè)模板數(shù)據(jù)渲染好的字符串。 教程目錄1.手把手教你從零寫一個(gè)簡(jiǎn)單的 VUE2.手把手教你從零寫一個(gè)簡(jiǎn)單的 VUE--模板篇 Hello,我又回來了,上一次的文章教會(huì)了大家如何書寫一個(gè)簡(jiǎn)單 VUE,里面實(shí)現(xiàn)了VUE 的...

    feng409 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<