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

資訊專欄INFORMATION COLUMN

Write Lean Programs

gnehc / 2706人閱讀

摘要:使用匿名內部類是最經典的使用方法之一。可以通過表達式替代匿名內部類,簡化設計。

OO makes code understandable by encapsulating moving parting, but FP makes code understandable by minimizing moving parts. -Michael Feathers

劉光聰,程序員,敏捷教練,開源軟件愛好者,具有多年大型遺留系統的重構經驗,對OOFPDSL等領域具有濃厚的興趣。

GitHub: https://github.com/horance-liu

Email: horance@outlook.com

Product Repository

First Attempt: The Worst Implement

需求1:在倉庫中查找所有顏色為紅色的產品

public ArrayList findAllRedProducts(ArrayList repo) {
  ArrayList result = new ArrayList();
  for (int i=0; i

指令式(Imperative)

缺乏編譯時類型安全性檢查

實現類型

硬編碼

重復設計

Second Attempt: Using for-each
public List findAllRedProducts(List repo) {
  List result = new ArrayList<>();
  for (Product p : repo) {
    if (p.getColor() == Color.RED) {
      result.add(p);
    }
  }
  return result;
}
Third Attempt: Parameterizing

需求2:在倉庫中查找所有顏色為綠色的產品

Copy-Paste是大部分程序員最容易犯的毛病,為此引入了大量的重復代碼。

public List findAllGreenProducts(List repo) {
  List result = new ArrayList<>();
  for (Product p : repo) {
    if (p.getColor() == Color.GREEN) {
      result.add(p);
    }
  }
  return result;
}

為了消滅Hard Code和重復代碼,得到可重用的代碼,可以引入簡單的參數化設計。

public List findProductsByColor(List repo, Color color) {
  List result = new ArrayList<>();
  for (Product p : repo) {
    if (p.getColor() == color) {
      result.add(p);
    }
  }
  return result;
}
Forth Attempt: Parameterizing with Every Attribute You Can Think Of

需求3:查找所有重量小于10的所有產品

大部分程序員依然會使用Copy-Paste解決這個問題,拒絕Copy-Paste的陋習,最具實效的一個辦法就是把Copy-Paste的快捷鍵失效,當每次嘗試Copy-Paste時提醒自己做更好的設計。

public List findProductsBelowWeight(List repo, int weight) {
  List result = new ArrayList<>();
  for (Product p : repo) {
    if (p.getWeight() < weight) {
      result.add(p);
    }
  }
  return result;
}

為了消除兩者重復的代碼,通過簡單的參數化往往不能完美解決這類問題,相反會引入額外的復雜度。

public List findProducts(List repo, Color color, int weight, boolean flag) {
  List result = new ArrayList<>();
  for (Product p : repo) {
    if ((flag && p.getColor() == color) ||
       (!flag && p.getWeight() < weight)) {
      result.add(p);
    }
  }
  return result;
}

日常工作中這樣的實現手法非常普遍,函數的參數列表隨著需求增加不斷增加,函數邏輯承擔的職責越來越多,邏輯也變得越來越難以控制。

Firth Attempt: Abstracting over Criteria

為此需要抽取出隱藏的概念,使其遍歷的算法與查找的標準能夠獨立地變化,將行為參數化

public interface ProductSpec {
  boolean satisfy(Product product);
}

此刻findProducts的算法邏輯得到封閉。

public List findProducts(List repo, ProductSpec spec) {
  List result = new ArrayList<>();
  for (Product p : repo) {
    if (spec.satisfy(p)) {
      result.add(p);
    }
  }
  return result;
}

通過可復用的Functor來封裝各種變化,讓變化的因素控制在最小的范圍內。

public class ColorSpec implements ProductSpec {
  private Color color;

  public ColorSpec(Color color) {
    this.color = color;
  }

  @Override
  public boolean satisfy(Product product) {
    return product.getColor() == color;
  }
}
public class BelowWeightSpec implements ProductSpec {
  private int limit;

  public BelowWeightSpec(int limit) {
    this.limit = limit;
  }

  @Override
  public boolean satisfy(Product product) {
    return product.getWeight() < limit;
  }
}

用戶的接口也變得簡單多了,而且富有表現力。

List products = findProducts(repo, new ColorSpec(RED));

這是經典的OO設計,如果熟悉設計模式的讀者對此已經習以為常了。設計模式是好東西,但常常被人依葫蘆畫瓢,死板照抄,甚至被濫用。事實上,引入或去除設計模式是一個很自然的過程。與大師們交流,問究此處為何引入設計模式,得到的答案:直覺。忘記所有設計模式吧,管它是不是模式,如果設計是簡單的,它這就是模式。

至此,代碼另外還有一個明顯的壞味道,ColorSpecBelowWeightSpec都需要繼承ProductSpec,都需要定義一個構造函數和一個私有的字段,并重寫satisfy方法,這是一種典型的重復現象:重復型結構

Java缺乏閉包的支持,程序員不得不承受這樣的煩惱,但此刻暫時不關心,繼續前進。

Sixth Attempt: Composite Criteria

需求4:查找所有顏色為紅色或者綠色,并且重量小于10的產品

按照既有的代碼結構,往往易于設計出類似ColorAndBelowWeightSpec的實現。

public class ColorAndBelowWeightSpec implements ProductSpec {
  private Color color1;
  private Color color2;
  private int limit;

  public ColorAndBelowWeightSpec(Color color1, Color color2, int limit) {
    this.color1 = color1;
    this.color2 = color2;
    this.limit = limit;
  }

  @Override
  public boolean satisfy(Product p) {
    return (p.getColor() == color1 || p.getColor() == color2) 
        && (p.getWeight() < limit);
  }
}

存在兩個明顯的壞味道:

類名中包含And往往是違背單一職責的信號燈

ColorAndBelowWeightSpec的實現與ColorSpecBelowWeightSpec之間存在明顯的重復

此刻,需要尋找更本質的抽象來表達設計,引入and/or的語義模型。

Composite Spec: AndSpec, OrSpec

Atomic Spec:ColorSpec, BeblowWeightSpec

publc class AndSpec implements ProductSpec {
  private List specs = new ArrayList<>();

  public AndSpec(ProductSpec... specs) {
    this.specs.addAll(Arrays.asList(specs));
  }

  @Override
  public boolean satisfy(Product p) {
    for (ProductSpec spec : specs) {
      if (!spec.satisfy(p))
        return false;
    }
    return true;
  }
}
publc class OrSpec implements ProductSpec {
  private List specs = new ArrayList<>();

  public OrSpec(ProductSpec... specs) {
    this.specs.addAll(Arrays.asList(specs));
  }

  @Override
  public boolean satisfy(Product p) {
    for (ProductSpec spec : specs) {
      if (spec.satisfy(p))
        return true;
    }
    return false;
  }
![clipboard.png](/img/bVtn06)

}

可以通過AndSpec組合ColorSpec, BelowWeightSpec來實現需求,簡單漂亮,并且富有表達力。

List products = findProducts(repo, new AndSpec(
  new OrSpec(new ColorSpec(RED), new ColorSpec(Greeen)), new BelowWeightSpec(10));

此時設計存在兩個嚴重的壞味道:

AndSpecOrSpec存在明顯的代碼重復

大堆的new讓人眼花繚亂

Seventh Attempt: Extract Parent

先嘗試消除AndSpecOrSpec存在的代碼重復,OO設計的第一個直覺就是通過抽取基類。

class CombinableSpec implements ProductSpec {
  private List specs = new ArrayList<>();
  private boolean shortcut;

  protected CombinableSpec(List specs, boolean shortcut) {
    this.specs.addAll(specs);
    this.shortcut = shortcut;
  }

  @Override
  public boolean satisfy(Product p) {
    for (ProductSpec spec : specs) {
      if (spec.satisfy(p) == shortcut)
        return shortcut;
    }
    return !shortcut;
  }
}

通過參數化配置,復用CombinableSpec的實現。

publc class AndSpec extends CombinableSpec {
  public AndSpec(ProductSpec... specs) {
    super(Arrays.asList(specs), false);
  }
}
publc class OrSpec extends CombinableSpec {
  public OrSpec(ProductSpec... specs) {
    super(Arrays.asList(specs), true);
  }
}

如何評判boolean接口的使用呢?在不損傷可理解性的前提下,為了消除重復的設計是值得推薦的。boolean接口的可理解性關鍵依賴于調用點與函數接口之間的距離,如果在同一個文件,同一個類,并能在一個頁面顯示的,是完全可以接受的。

Eighth Attempt: Decorate Criteria

需求5:查找所有顏色為不是紅色的產品

publc class NotSpec implements ProductSpec {
  private ProductSpec spec;

  public NotSpec(ProductSpec spec) {
    this.spec = spec;
  }

  @Override
  public boolean satisfy(Product p) {
    return !spec.satisfy(p);
  }
}

NotSpec是一種修飾了的ProductSpec,同時也使得用戶的接口也變得更加人性化了。

List products = findProducts(repo, new NotSpec(new ColorSpec(RED)));

Ninth Attempt: Using Static Factory to DSL

之前遺留了一個問題,一大堆眼花繚亂的new使得代碼失去了部分的可讀性。

List products = findProducts(repo, new AndSpec(
  new OrSpec(new ColorSpec(RED), new ColorSpec(Greeen)), new BelowWeightSpec(10));

可以引入DSL改善程序的可讀性,讓代碼更具表達力。

List products = findProducts(repo, and(or(color(RED), color(GREEN)), belowWeight(10)));

上述的DSL可以使用static factory的設計手段簡單實現。按照慣例,可以建立類似于ProductSpecs的工具類,將這些工廠方法搬遷到工具類中去。

接口與對應工具類的對稱性設計在Java社區中應用非常廣泛,例如標準庫中的java.util.Collection/java.util.Collections的設計。

public interface ProductSpec {
  boolean satisfy(Product p);
}
public final class ProductSpecs {
  public static ProductSpec color(final Color color) {
    return new ProductSpec() {
      @Override
      public boolean satisfy(Product p) {
        return p.getColor() == color;
      }
    };
  }

  public static ProductSpec belowWeight(final int limit) {
    return new ProductSpec() {
      @Override
      public boolean satisfy(Product p) {
        return p.getWeight() < limit;
      }
    };
  }

  public static ProductSpec and(ProductSpec... specs) {
    return new CombinableSpec(Arrays.asList(specs), false);
  }

  public static ProductSpec or(ProductSpec... specs) {
    return new CombinableSpec(Arrays.asList(specs), true);
  }

  public static ProductSpec not(final ProductSpec spec) {
    return new ProductSpec() {
      @Override
      public boolean satisfy(Product p) {
        return !spec.satisfy(p);
      }
    };
  }

  private ProductSpecs() {
    throw new AssertionError("no instances");
  }
}

此外,使用匿名內部類,可以得到意外的驚喜。通過有限地引入閉包的概念,從而避免了類似Firth Attempt/Sixth Attempt的設計中引入多余的構造函數和成員變量的復雜度,從而消除了部分的結構性重復的壞味道。

當然,要讓這些static factory可見,需要import static導入這些方法。

import static practical.programming.overview.ProductSpec.*;

List products = findProducts(repo, not(and(color(RED), belowWeight(10)));
Tenth Attempt: Moving Static Factory into Interface

使用Java8可以將這些工廠方法直接搬遷到ProductSpec的接口中去,這樣做至少得到兩個好處。

可以刪除ProductSpecs的工具類

使的接口和靜態方法(尤其靜態工廠方法)關系更加緊密

Java8并沒有因為comparing等靜態工廠方法的增強而建立Comparators的工具類,而是直接將它們集成在Comparator的接口中,這是自Java8之后思維的一個新的轉變(Comparator.comparing的實現留作作業鞏固今天所學知識)。

對于本例,可以將ProductSpecs刪除,將所有靜態工廠方法搬遷到ProductSpec中去。

public interface ProductSpec {
  boolean satisfy(Product p);

  static ProductSpec color(Color color) {
    return new ProductSpec() {
      @Override
      public boolean satisfy(Product p) {
        return p.getColor() == color;
      }
    };
  }

  static ProductSpec belowWeight(int limit) {
    return new ProductSpec() {
      @Override
      public boolean satisfy(Product p) {
        return p.getWeight() < limit;
      }
    };
  }

  static ProductSpec and(ProductSpec... specs) {
    return new CombinableSpec(Arrays.asList(specs), false);
  }

  static ProductSpec or(ProductSpec... specs) {
    return new CombinableSpec(Arrays.asList(specs), true);
  }

  static ProductSpec not(ProductSpec spec) {
    return new ProductSpec() {
      @Override
      public boolean satisfy(Product p) {
        return !spec.satisfy(p);
      }
    };
  }
}
Eleventh Attempt: Using Null Object

需求6無條件過濾掉不過濾查找所有產品

import static practical.programming.overview.ProductSpec.*;

List products = findProducts(repo, always(false));
public interface ProductSpec {
  boolean satisfy(Product p);

  static ProductSpec always(boolean bool) {
    return new ProductSpec() {
      @Override
      public boolean satisfy(Product p) {
        return bool;
      }
    };
  }
}

至此,ProductSpec存在如下一些類型:

Composite Specs: and, or

Decorator Specs: not

Atomic Specs: always, color, beblowWeight

Twelfth Attempt: Using Lambda Expression

Java8可以使用Lambda表達式改善設計,增強表達力。

List products = findProducts(repo, (Product p) -> p.getColor() == RED);

通過類型推演,可以進一步省略Labmda表達式中參數的類型信息。

List products = findProducts(repo, p -> p.getColor() == RED);

當然,你可以通過提取static factory,構造DSL復用這些Lambda表達式。

@FunctionalInterface
public interface ProductSpec {
  boolean satisfy(Product p);

  static ProductSpec color(Color color) {
    return p -> p.getColor() == color;
  }

  static ProductSpec weightBelow(int limit) {
    return p -> p.getWeight() < limit;
  }
}
List products = findProducts(repo, color(RED));

其中,@FunctionalInterface注解標注了ProductSpec是一個函數式接口,其抽象方法boolean satisfy(Product p)的原型描述了lambda表達式的Function Descriptor

Thirteenth Attempt: Chaining Speciafications

遺留了一個問題: 如何替換匿名內部類,使用lambda實現 and/or/not/always的語義?

@FunctionalInterface
public interface ProductSpec {
  boolean satisfy(Product p);

  default ProductSpec negate() {
    return p -> !satisfy(p);
  }

  default ProductSpec and(ProductSpec other) {
    return (p) -> satisfy(p) && other.satisfy(p);
  }

  default ProductSpec or(ProductSpec other) {
    return (p) -> satisfy(p) || other.satisfy(p);
  }

  static ProductSpec always(boolean bool) {
    return p -> bool;
  }

  static ProductSpec color(Color color) {
    return p -> p.getColor() == color;
  }

  static ProductSpec belowWeight(int limit) {
    return p -> p.getWeight() < limit;
  }
}

這里引入了Java8一個重要的設計工具:default method,簡單漂亮,并巧妙地實現DSL的設計,用戶接口變得更加流暢、友好。

List products = findProducts(repo, color(RED).and(belowWeight(10)));

Java8支持default method,擴展了interface原來的語義,從而隱式地支持了組合式設計,使的OO的設計更加完善和強大。

Fourteenth attempt: Using Method Reference

需求7:查找所有偽劣的產品

List products = findProducts(repo, p -> p.fake());

可以使用Method Reference進一步改善lambda的表達力。

List products = findProducts(repo, Product::fake);
Fifteenth attempt: Abstracting over Type

泛化類型信息,讓算法更具有通用性,并進一步增強代碼的可復用性。

public static  List filter(List list, Predicate p) {
  List result = new ArrayList<>();
  for (T e : list) {
  if (p.test(e)) {
      result.add(e);
    }
  }
  return result;
}

這樣的實現存在一個明顯的問題:泛型參數缺乏型變的能力。通過對泛型參數實施無限定類型通配符的修飾,從而使的算法實現更加具有彈性和通用性。

public static  List filter(List list, Predicate p) {
  List result = new ArrayList<>();
  for (T e : list) {
    if (p.test(e)) {
      result.add(e);
    }
  }
  return result;
}
Sixteenth: Maximize Reusability

and, or, not, always在代數系統中具有穩定的抽象,為此需要進一步重構,以便最大化代碼的可復用性。這樣當需要建立諸如NumberSpec, FruitSpec時無需重復地再寫一遍and, or, not, always的實現。

為此,建立更為抽象的Predicate的概念,并將通用的、抽象的negate, and, or, always搬遷到Predicate中去,使其具有更大的可復用性。

@FunctionalInterface
public interface Predicate {
  boolean test(T t);

  default Predicate negate() {
    return p -> !satisfy(p);
  }

  default Predicate and(Predicate other) {
    return p -> satisfy(p) && other.satisfy(p);
  }

  default Predicate or(Predicate other) {
    return p -> satisfy(p) || other.satisfy(p);
  }
  
  static Predicate always(boolean bool) {
    return p -> bool;
  }
}

同時,將領域內的color, belowWeight等原子放回ProductSpecs工具類中去(因為不存在ProductSpec的接口了),讓領域內的lambda表達式具有更大的復用性。

public final class ProductSpecs {
  public static Predicate color(Color color) {
    return p -> p.getColor() == color;
  }

  public static Predicate belowWeight(int limit) {
    return p -> p.getWeight() < limit;
  }

  private ProductSpecs() {
    throw new AssertionError("no instances");
  }
} 

至此,可復用的基礎設施便從領域中剝離出來,使其具有更高度的可重用性。

Seventeenth Attempt: Using Stream API

Java8可以使用集合庫的Stream復用代碼。

import static java.util.stream.Collectors.toList;

repo.stream()
    .filter(p -> p.getColor() == RED && p.getPrice() < 10)
    .collect(toList());

如果要支持并發,則可以構建parallelStream

import static java.util.stream.Collectors.toList;

repo.parallelStream()
    .filter(p -> p.getColor() == RED && p.getPrice() < 10)
    .collect(toList());

集合類通過stream, parallelStream工廠方法創建Stream之后,其操作可分為2種基本類型:

Transformation:其返回值為Stream類型

Action:其返回值不是Stream類型

通過Stream的機制,實現了集合類的惰性求值,直至Action才真正地開始執行計算。Transformation從某種意義上,可以看成是StreamBuilder,直至Action啟動執行。

Eighteenth attempt: Replace Java with Scala

Scala語言是一門跨越OOFP的一個混血兒,可以方便地與Java進行互操作。在Scala中,函數作為一等公民,使用Lambda是一個很自然的過程。當你熟悉了Scala,我相信你絕對會放棄Java,放棄Java8,猶如作者本人一樣。

repo.filter(p => p.color == RED && p.weight < 10)

遺留了三個問題:

如何復用lambda表達式?

如何實現 and/or/not的語義?

如何實現 always的語義?

Nineteenth Attempt: Abstracting Control Structure

引入靜態工廠方法及其操作符重載的機制構造內部DSL

import ProductSpec._

repo.filter(color(RED) && belowWeight(10))
object ProductSpec {
  def color(color: Color) = ???
  def bebowWeight(limit: Int) = ???
}

如何替換實現???,并讓其具有&&, ||, !的語義呢?

object ProductSpec {
  def color(color: Color) = new Predicate((p: Product) => p.color == color)
  def bebowWeight(limit: Int) = new Predicate((p: Product) => p.weight < limit)
}

Predicate一個擴展匿名函數A => Boolean的子類,其中,從面向對象的角度看,A => Boolean的類型為Function[A, Boolean]

class Predicate[A](pred: A => Boolean) extends (A => Boolean) {
  override def apply(a: A) = pred(a)

  def &&(that: A => Boolean) = new Predicate[A](x => pred(x) && that(x))
  def ||(that: A => Boolean) = new Predicate[A](x => pred(x) || that(x))
  def unary_! = new Predicate[A](x => !pred(x))
}

其中!是一個一元操作符。

Twentieth Attempt: Using Companion Object

always靜態工廠方法,可以搬遷到Predicate的伴生對象中去。

object Predicate {
  def always[A](bool: Boolean) = new Predicate[A](_ => bool)
}

Predicate的設計既使用了OO的特性,又引入了FP的思維,Scala使其兩者如此和諧、完美,簡直不可思議。

Conclusion

世界是多樣性的,計算機工業也不僅僅只存在一種方法論。在我的哲學觀里,OO和FP之間并不矛盾,而是一個和諧的,相互補充的統一體。

除了C++語言之外,使得我最偏愛Scala,多范式,一個問題存在多種解決方案等等思維習慣,給了程序員最靈活、最自由的空間。

Review Comprator.comparing

以標準庫Collections.sort,及其ComparatorJava8中的增強,及其Comparator.comparing的泛型定義復習今天所學知識。

public final class Collectins {
  private Collectins() {
  }
  
  public static  void sort(List list, Comparator c) {
    list.sort(c);
  }
} 

使用匿名內部類是Collectins.sort最經典的使用方法之一。

Collections.sort(products, new Comparator() {
  @Override
  public int compare(Product p1, Product p2) {
    return p1.getName().compareTo(p2.getName);
  }
});

可以通過lambda表達式替代匿名內部類,簡化設計。

Collections.sort(products, (Product p1, Product p2) -> p1.getName().compareTo(p2.getName));

通過類型推演,但依然得到編譯器類型安全的保護。

Collections.sort(products, (p1, p2) -> p1.getName().compareTo(p2.getName));

通過Comprator.compring的靜態工廠方法,改善表達力。

Collections.sort(persons, comparing(p -> p.getName()))

通過Function Reference的機制,進一步改善表達力。

Collections.sort(persons, comparing(Person::getName()))

其中,Comprator.compring的實現為:

@FunctionalInterface
public interface Comparator {
  int compare(T o1, T o2);

  static > Comparator 
    comparing(Function extractor) {
      return (c1, c2) -> extractor.apply(c1).compareTo(extractor.apply(c2));
  }
}

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65592.html

相關文章

  • SegmentFault 技術周刊 Vol.16 - 淺入淺出 JavaScript 函數式編程

    摘要:函數式編程,一看這個詞,簡直就是學院派的典范。所以這期周刊,我們就重點引入的函數式編程,淺入淺出,一窺函數式編程的思想,可能讓你對編程語言的理解更加融會貫通一些。但從根本上來說,函數式編程就是關于如使用通用的可復用函數進行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數式編程(Functional Programming),一...

    csRyan 評論0 收藏0
  • 一步一步教你如何搭建自己的視頻聚合站

    摘要:不過因為各個平臺互相挖人的關系,導致關注的一些主播分散到了各個直播平臺,來回切換有點麻煩,所以萌生了做一個視頻聚合站的想法。后續我們會對這三個部分的功能做逐一展開說明。正則處理要求比較高,但是幾乎能應對所有的情況,屬于大殺器。 前言 作為一個爐石傳說玩家,經常有事沒事開著直播網站看看大神們的精彩表演。不過因為各個平臺互相挖人的關系,導致關注的一些主播分散到了各個直播平臺,來回切換有點麻...

    justjavac 評論0 收藏0
  • 精益思想Lean thinking

    摘要:例日本的企業由于種種原因把制作放在本土,這樣產品需要遠洋運輸到客戶手中。讓生產過程流動起來,減少部門之間的浪費。盡善盡美快速反饋,積極改進。 精益思想Lean thinking 浪費專指消耗了資源而不創造價值的一切人類活動 需要糾正的錯誤 生產了無需求的產品 由此造成的庫存和積壓 不必要的工序 員工的盲目走動 貨物從一地到另一地的盲目搬運 由于上道工序發送傳遞不及時,使做下一道工序的...

    王軍 評論0 收藏0
  • 關于JAVA中順序IO的基本操作

    摘要:關于中順序的基本操作關于中順序的基本操作寫在前面最近研究一下中的順序,在網絡上找了一會兒,發現少有詳細的介紹,顧此在此處說說順序,才學疏淺,如有不對,望賜教。上述代碼中標記位置中,返回下一次操作時的位置。關于JAVA中順序IO的基本操作 寫在前面 最近研究一下JAVA中的順序IO,在網絡上找了一會兒,發現少有詳細的介紹,顧此在此處說說順序IO,才學疏淺,如有不對,望賜...

    EscapedDog 評論0 收藏0
  • 精益 React 學習指南 (Lean React)- 4.1 react 代碼規范

    書籍完整目錄 4.1 react 代碼規范 showImg(https://segmentfault.com/img/bVyE9m); 關于 基礎規范 組件結構 命名規范 jsx 書寫規范 eslint-plugin-react 關于 在代碼的設計上,每個團隊可能都有一定的代碼規范和模式,好的代碼規范能夠提高代碼的可讀性便于協作溝通,好的模式能夠上層設計上避免不必要的 bug 出現。本節會參考...

    zhangqh 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<