架構師日記—聊聊開發必掌握的那些實踐技能
來源:京東技術導讀
實際開發場景中總是會遇到各種各樣棘手的問題,那麼開發者如何儘可能減少不必要的問題呢?本文不僅為架構師們提供了豐富的理論知識和實踐經驗,還透過舉例和案例分析深入淺出地闡述了這些實踐技能的重要性和應用方法,無論是初學者還是有一定經驗的開發者,都可以從中獲得一定的啟示。
不同程式語言,擁有不同的特性和規約,下面就以JAVA語言為例,細數那些開發過程中容易被人忽略,但必須掌握的知識點和實踐技能。
基礎篇
2.1 關於命名
•蛇形命名法(snake case):又叫下劃線命名法,使用下劃線,單詞小寫,比如:my_system;
•駝峰命名法(camel case):按照單詞首字母區分大小寫,又可細分為大駝峰命名法和小駝峰命名法,比如:MySystem,mySystem;
•匈牙利命名法(HN case):屬性+型別+描述,比如:nLength,g_cch,hwnd;
•帕斯卡命名法(Pascal case):全部首字母大寫,等同於大駝峰命名法,比如:MySystem;
•脊柱命名法(spinal case):使用中劃線,比如:my-system;
2.1.1 命名字典
見名知意:好的命名就是一種註釋。
建議研發同學將業內常見業務場景的命名熟記,當然,已經有不少前輩總結過了,這裡不再做過多的說明。這裡摘錄如下,可供參考:
其他類命名:Mode,Type,Invoker,Invocation,Initializer,Future,Promise,Selector,Reporter,Constants,Accessor,Generator
2.1.2 命名實踐
工程通用命名規則都有哪些呢?不同的語言可能會有不同的習慣,以Java語言的駝峰命名規範舉例:
5. 常量名全部大寫;
規範比較抽象,先來看看不好的命名有哪些呢?
5. 大小寫,數字,縮寫混亂:String waitRPCResponse1 = "極易出錯型";
除了標準的規範之外,在實際的開發過程中還會有一些引發困擾的實際案例。
包裝類和基本資料型別的預設值是不一樣的,前者是null,後者依據不同型別其預設值也不一樣。從資料嚴謹的角度來講,包裝類的null值能夠表示額外資訊,從而更加安全。比如可以規避基本型別的自動拆箱,導致的NPE風險以及業務邏輯處理異常風險。所以成員變數必須使用包裝資料型別,基本資料型別則在區域性變數的場景下使用。
public <PropertyType> get<PropertyName>(); public void set<PropertyName>(<PropertyType> p)
public boolean is<PropertyName>(); public void set<PropertyName>(boolean p)
由於各種RPC框架和物件序列化工具對於布林型別變數的處理方式存在差異,就容易造成程式碼移植性問題。最常見的json序列化庫Jackson和Gson之間就存在相容性問題,前者是透過透過反射遍歷出該類中的所有getter方法,透過方法名擷取獲得到物件的屬性,後者則是透過反射直接遍歷該類中的屬性。為了規避這種差異對業務的影響,建議所有成員變數都不要以is開頭,防止序列化結果出現不預知的情況發生。
(1)一類是同一個jar包出現了多個不同的版本。應用選擇了錯誤的版本導致jvm載入不到需要的類或者載入了錯誤版本的類;(藉助maven管理工具相對容易解決)
public class SkuKey implements Serializable { @JsonProperty(value = "sn") @ApiModelProperty(name = "stationNo", value = " 門店編號", required = true) private Long stationNo; @JsonProperty(value = "si") @ApiModelProperty(name = "skuId", value = " 商品編號", required = true) private Long skuId; // 省略get/set方法}
2.2 關於註釋
1. 冗餘式:如果一個函式,讀者能夠很容易的就讀出來程式碼要表達的意思,註釋就是多餘的;
2. 錯誤式:如果註釋的不清楚,甚至出現歧義,那還不如不寫;
3. 簽名式:類似“add by liuhuiqing 2023-08-05”這種註釋,容易過期失效而且不太可信(不能保證所有人每次都採用這種方式註釋),其功能完全可以由git程式碼管理工具來實現;
4. 長篇大論式:程式碼塊裡,夾雜了大篇幅的註釋,不僅影響程式碼閱讀,而且維護困難;
5. 非本地註釋:註釋應該在離程式碼實現最近的地方,比如:被呼叫的方法註釋就由方法本身來維護,呼叫方無需對方法做詳細的說明;
2.3 關於分層
在ISO(International Standardization Organization)於1981年制定網路通訊七層模型(Open System Interconnection Reference Model,OSI/RM)之前,計算機網路中存在眾多的體系結構,其中以IBM公司的SNA(系統網路體系結構)和DEC公司的DNA(DigitalNetworkArchitecture)數字網路體系結構最為著名。
最早之前,各個廠家提出的不同標準都是以自裝潢置為基礎的,使用者在選擇產品的時候就只能用同一家公司的,因為不同公司間大家的標準不一樣,工作方式也可能不一樣,結果就是不同廠商的網路產品間,可能會出現不相容的情況。如果說同一家的公司的產品都能滿足使用者的需求的話,那就看哪家公司實力強點,實力強的,使用者粘性高的,使用者自然也不會說什麼,問題是一家公司並不是對所有的產品都擅長。這就會導致廠商和使用者都面臨著痛苦的煎熬。類比一下當前手機充電介面協議(Micro USB介面、Type- c介面、Lightning介面),手頭總是要備有各種充電線的場景,就能深刻理解標準的意義了。
2.4 小結
實踐篇
下面就從程式的擴充套件性,維護性,安全性以及效能等幾個重要質量指標,來學習那些經典的實踐案例。
3.1 類定義
3.1.1 常量定義
常量是一種固定值,不會在程式執行期間發生改變。你可以使用列舉(Enum)或類(Class)來定義常量。
如果你需要定義一組相關的常量,那麼使用列舉更為合適。列舉從安全性和可操作性(支援遍歷和函式定義)上面擁有更大的優勢。
public enum Color { RED, GREEN, BLUE;}
如果你只需要定義一個或少數幾個只讀的常量,那麼使用類常量更為簡潔和方便。
public class MyClass { public static final int MAX_VALUE = 100;}
public abstract class ObjectHelper {
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
}
為了實現不需要例項化物件的約束,開發者在類定義時,最好加上abstract關鍵字進行宣告限定,這也是為什麼spring等開源工具類大都使用abstract關鍵字修飾。
3.1.3 JavaBean
JavaBean的定義有兩種常見實現方式:手動編寫和自動生成。
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
import lombok.Data;
@NoArgsConstructor
@Data
@Accessors(chain = true)
public class Person {
private String name;
private int age;
}
public final class String implements Serializable, Comparable<String>, CharSequence {
}
java.lang.Stringjava.lang.Mathjava.lang.Booleanjava.lang.Characterjava.util.Datejava.sql.Datejava.lang.Systemjava.lang.ClassLoader
1. 直接作為引數傳遞給方法或建構函式;
2. 用於實現某個介面或抽象類的匿名例項;
public class Example { public static void main(String[] args) { // 建立一個匿名內部類 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("Hello, World!"); } }; // 呼叫匿名內部類的方法 runnable.run(); }}
/** * 關鍵定義的類是不可變類 * 將所有成員變數透過引數的形式定義 * 預設會生成全部引數的構造方法 * @param name * @param age */public record Person(String name, int age) { public Person{ if(name == null){ throw new IllegalArgumentException("提供緊湊的方式進行引數校驗"); } } /** * 定義的類中可以定義靜態方法 * @param name * @return */ public static Person of(String name) { return new Person(name, 18); }}
Person person = new Person("John", 30);// Person person = Person.of("John");String name = person.name();int age = person.age();
public List<Person> sortPeopleByAge(List<Person> people) {
record Data(Person person, int age){};
return people.stream()
.map(person -> new Data(person, computAge(person)))
.sorted((d1, d2) -> Integer.compare(d2.age(), d1.age()))
.map(Data::person)
.collect(toList());
}
public int computAge(Person person) {
return person.age() - 1;
}
1. final修飾類,這樣類就無法被繼承了;
2. package-private類,可以控制只能被同一個包下的類繼承;
sealed class SealedClass permits SubClass1, SubClass2 {
}
class SubClass1 extends SealedClass {
}
class SubClass2 extends SealedClass {
}
3.2 方法定義
public class MyClass {
private int myInt;
private String myString;
// 構造方法
public MyClass(int myInt, String myString) {
this.myInt = myInt;
this.myString = myString;
}
}
class Animal {
public void makeSound() {
System.out.println("Animal is making a sound");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow");
}
}
public class Main {
public static void main(String[] args) {
Animal myCat = new Cat();
myCat.makeSound(); // 輸出 "Meow"
}
}
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
int result1 = calculator.add(2, 3);
double result2 = calculator.add(2.5, 3.5);
System.out.println(result1); // 輸出 5
System.out.println(result2); // 輸出 6.0
}
}
Java 8 引入了 Lambda 表示式,可以用來實現類似匿名方法的功能。Lambda 表示式是一種匿名函式,可以作為引數傳遞給方法,或者直接作為一個獨立表示式使用。
public static void main(String args[]) {
List<String> names = Arrays.asList("hello", "world");
// 使用 Lambda 表示式作為引數傳遞給 forEach 方法
names.forEach((String name) -> System.out.println("Name: " + name));
// 使用 Lambda 表示式作為獨立表示式使用
Predicate<String> nameLengthGreaterThan5 = (String name) -> name.length() > 5;
boolean isLongName = nameLengthGreaterThan5.test("John");
System.out.println("Is long name? " + isLongName);
}
3.3 物件定義
1. 控制資源的使用:透過執行緒同步來控制資源的併發訪問。
2. 控制例項產生的數量:達到節約資源的目的。
public enum Singleton {
INSTANCE;
public void someMethod() {
// ...其他程式碼...
}
}
1. 將物件的狀態儲存在不可變物件中:String、Integer等就是內建的不可變物件型別;
2. 將物件的狀態儲存在final變數中:final變數一旦被賦值就不能被修改;
Collections.unmodifiableList(new ArrayList<>());
public class Pair<A,B> {
public final A first;
public final B second;
public Pair(A a, B b) {
this.first = a;
this.second = b;
}
public A getFirst() {
return first;
}
public B getSecond() {
return second;
}
}
三元組實現
public class Triplet<A,B,C> extends Pair<A,B>{
public final C third;
public Triplet(A a, B b, C c) {
super(a, b);
this.third = c;
}
public C getThird() {
return third;
}
public static void main(String[] args) {
// 表示姓名,性別,年齡
Triplet<String,String,Integer> triplet = new Triplet("John","男",18);
// 獲得姓名
String name = triplet.getFirst();
}
}
多元組實現
public class Tuple<E> {
private final E[] elements;
public Tuple(E... elements) {
this.elements = elements;
}
public E get(int index) {
return elements[index];
}
public int size() {
return elements.length;
}
public static void main(String[] args) {
// 表示姓名,性別,年齡
Tuple<String> tuple = new Tuple<>("John", "男", "18");
// 獲得姓名
String name = tuple.get(0);
}
}
1. 儲存多個資料元素:Tuple可以儲存多個不同型別的資料元素,這些元素可以是基本型別、物件型別、陣列等;
2. 簡化程式碼:Tuple可以使程式碼更加簡潔,減少重複程式碼的編寫。透過Tuple,開發者可以將多個變數打包成一個物件,從而減少了程式碼量;
3. 提高程式碼可讀性:Tuple可以提高程式碼的可讀性。透過Tuple,開發者可以將多個變數打包成一個物件,從而使程式碼更加易讀;
NamedTuple namedTuple = Tuples.named("person", "name", "age");
1. 儘量重用物件。由於系統不僅要花時間生成物件,以後可能還需花時間對這些物件進行垃圾回收和處理,因此,生成過多的物件將會給程式的效能帶來很大的影響,重用物件的策略有快取物件,也可以針對具體場景進行定向最佳化,比如使用StringBuffer代替字串拼接的方式;
2. 儘量使用區域性變數。呼叫方法時傳遞的引數以及在呼叫中建立的臨時變數都儲存在棧中,速度較快。其他變數,如靜態變數、例項變數等,都在堆中建立,速度較慢;
值型別(value types)的概念是表示純資料聚合,這會刪除常規物件的功能。因此只有純資料而沒有身份。當然,這意味著也失去了使用物件標識可以實現的功能。由於不再有物件標識,可以放棄指標,改變值型別的一般記憶體佈局。下面將物件引用和值型別記憶體佈局進行對比。
圖1.
•效能增強透過展平物件圖和移除間接來解決。這將獲得更高效的記憶體佈局和更少的分配和垃圾回收。
截止到2023年9月,Valhalla 專案仍在進行中,還沒有正式版本的釋出,這一創新專案值得期待的。
本文總結了軟體開發過程中經常用到的基礎常識,分為基礎篇和實踐篇兩個篇章,其中基礎篇中著重講述了類,方法,變數的命名規範以及程式碼註釋好壞的評判標準。實踐篇中從類,方法以及物件三個層面分析了常見的技術概念和落地實踐,希望這些常識能夠為讀者帶來一些思考和幫助。
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70024924/viewspace-3004258/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 架構師日記-聊聊開發必掌握的那些實踐技能架構
- 架構師日記-軟體高可用實踐那些事兒架構
- 架構師之路:一個架構師需要掌握的知識技能架構
- Java架構師必備技能:docker使用大全Java架構Docker
- 阿里架構師Peter老師講述Java程式設計師→架構師所需要掌握的技能阿里架構Java程式設計師
- 優秀前端開發工程師必須掌握的七大技能前端工程師
- 前端架構師破局技能,NodeJS 落地 WebSocket 實踐前端架構NodeJSWeb
- Java開發需要掌握哪些技術?Java程式設計師必備技能Java程式設計師
- 架構師必備:多維度查詢的最佳實踐架構
- 個人總結的一箇中高階Java開發工程師或架構師需要掌握的一些技能Java工程師架構
- java程式設計師進階架構師你必須掌握的架構知識體系Java程式設計師架構
- 架構師所需的硬實力與軟技能架構
- 面試筆記之必掌握的java核心技能點面試筆記Java
- 阿里大資料架構師必備技能,你“佩奇”了嘛?阿里大資料架構
- 走向架構師——1~3年java程式設計師面試必備的技能架構Java程式設計師面試
- 架構師技能圖譜架構
- 架構師必備的那些分散式事務解決方案!!架構分散式
- 架構必備技能第一談架構
- 【筆記】《app後臺開發運維和架構實踐》筆記APP運維架構
- [開發故事]架構師修煉 III - 掌握設計原則架構
- JAVA架構師那些事?Java架構
- 作為一名合格的JAVA架構師需要點亮那些技能樹?Java架構
- 聊聊程式設計師面試時,那些必須注意的事情程式設計師面試
- 架構師必備:巧用Canal實現非同步、解耦的架構架構非同步解耦
- 大資料開發工程師需要掌握什麼技能?大資料工程師
- 聊聊資料人的職場必備技能
- 掌握11項技能,你就是優秀的前端開發工程師前端工程師
- CSS中那些必須掌握的概念CSS
- React專案架構,掌握前端架構師的核心本領React架構前端
- 微服務架構技術棧:程式設計師必須掌握的微服務架構框架詳細解析微服務架構程式設計師框架
- 開發網站的必備技能網站
- 招聘golang開發&架構師Golang架構
- 開發者最佳實踐日•第13期-實踐微服務架構微服務架構
- [- Flutter必備 -] 聊聊那些彈框Flutter
- 乾淨架構在 Web 服務開發中的實踐架構Web
- 架構師害怕程式設計師知道的十項技能架構程式設計師
- 聊聊架構架構
- Java程式設計師微服務架構你必須要掌握的十個要點Java程式設計師微服務架構