成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java編程技巧:如何實(shí)現(xiàn)參數(shù)的輸入輸出?

開發(fā) 開發(fā)工具
Java沒有直接的輸入輸出參數(shù)機(jī)制,無法簡單地實(shí)現(xiàn)參數(shù)的輸入輸出功能,因此需要借助其它方法來實(shí)現(xiàn)。本文作者通過實(shí)踐總結(jié),分享利用方法參數(shù)、方法返回值、類字段等方法來實(shí)現(xiàn)參數(shù)的輸入輸出,并對比總結(jié)各自的優(yōu)缺點(diǎn)及使用場景。較長,可收藏后再看。

 Java沒有直接的輸入輸出參數(shù)機(jī)制,無法簡單地實(shí)現(xiàn)參數(shù)的輸入輸出功能,因此需要借助其它方法來實(shí)現(xiàn)。本文作者通過實(shí)踐總結(jié),分享利用方法參數(shù)、方法返回值、類字段等方法來實(shí)現(xiàn)參數(shù)的輸入輸出,并對比總結(jié)各自的優(yōu)缺點(diǎn)及使用場景。較長,可收藏后再看。

前言

軟件開發(fā)方法學(xué)的泰斗肯特·貝克(Kent Beck)曾說過:

我不是一個(gè)偉大的程序員,我只是一個(gè)具有良好習(xí)慣的優(yōu)秀程序員。

養(yǎng)成良好的習(xí)慣,尤其是不斷重構(gòu)的習(xí)慣,是每一個(gè)優(yōu)秀程序員都應(yīng)該具備的素質(zhì)。重構(gòu)(Refactoring)就是在不改變軟件現(xiàn)有功能的基礎(chǔ)上,通過調(diào)整程序的結(jié)構(gòu)、提高程序的質(zhì)量、優(yōu)化程序的性能……使其程序的設(shè)計(jì)模式和架構(gòu)更趨合理,從而提高軟件的穩(wěn)定性、擴(kuò)展性和維護(hù)性。

一 一個(gè)需要重構(gòu)的方法

需求描述:

需要把一個(gè)線串(一組經(jīng)緯度坐標(biāo)串),按照指定分段長度數(shù)組進(jìn)行按比例劃分(由于指定線串的長度較小,可以近似地認(rèn)為在幾何平面上,無需進(jìn)行球面距離換算)。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {

/** 常量相關(guān) */
/** 小數(shù)位數(shù) */
private static final int DIGIT_SCALE = 8;
/** 放大比例 */
private static final double ZOOM_SCALE = 10000000000L;
/** 幾何工廠 */
private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING));

/**
* 構(gòu)造方法
*/
private GeometryHelper() {
throw new UnsupportedOperationException();
}

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 檢查分段數(shù)量
if (Objects.isNull(segmentLengthes) || segmentLengthes.length < 1) {
return new LineString[] {lineString};
}

// 計(jì)算總共長度
double totalLength = Arrays.stream(segmentLengthes)
.map(segmentLength -> Math.max(segmentLength, 0.0D))
.sum();

// 計(jì)算目標(biāo)長度
double lineLength = lineString.getLength();
long[] targetLengthes = Arrays.stream(segmentLengthes)
.mapToLong(segmentLength -> getTargetLength(lineLength, totalLength, segmentLength))
.toArray();

// 初始化參數(shù)值
int index = 1;
Coordinate[] coordinates = lineString.getCoordinates();
Coordinate coordinate = coordinates[0];
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
// 添加線串坐標(biāo)
long addupLength = 0L;
List<Coordinate> coordinateList = new ArrayList<>();
coordinateList.add(coordinate);
for (; index < coordinates.length; index++) {
// 計(jì)算分段長度
long segmentLength = Math.round(coordinate.distance(coordinates[index]) * ZOOM_SCALE);

// 根據(jù)長度處理
boolean isBreak = true;
int compareResult = Long.compare(addupLength + segmentLength, targetLengthes[i]);
// 根據(jù)長度處理: 未達(dá)目標(biāo)長度
if (compareResult < 0) {
addupLength += segmentLength;
coordinate = coordinates[index];
coordinateList.add(coordinate);
isBreak = false;
}
// 根據(jù)長度處理: 超過目標(biāo)長度
else if (compareResult > 0) {
long deltaLength = targetLengthes[i] - addupLength;
coordinate = buildMiddleCoordinate(coordinate, coordinates[index], segmentLength, deltaLength);
}
// 根據(jù)長度處理: 等于目標(biāo)長度
else {
index++;
coordinate = coordinates[index];
}

// 是否跳出循環(huán)
if (isBreak) {
break;
}
}
coordinateList.add(coordinate);

// 設(shè)置線串對象
lineStrings[i] = buildLineString(coordinateList);
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, index, coordinate);

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 構(gòu)建線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param index 當(dāng)前序號
* @param coordinate 當(dāng)前坐標(biāo)
* @return 線串
*/
private static LineString buildLineString(Coordinate[] coordinates, int index, Coordinate coordinate) {
List<Coordinate> coordinateList = new ArrayList<>();
coordinateList.add(coordinate);
coordinateList.addAll(Arrays.asList(ArrayUtils.subarray(coordinates, index, coordinates.length)));
return buildLineString(coordinateList);
}

/**
* 構(gòu)建線串
*
* @param coordinateList 坐標(biāo)列表
* @return 線串
*/
private static LineString buildLineString(List<Coordinate> coordinateList) {
return GEOMETRY_FACTORY.createLineString(coordinateList.toArray(new Coordinate[0]));
}

/**
* 構(gòu)建中間坐標(biāo)
*
* @param coordinate1 坐標(biāo)1
* @param coordinate2 坐標(biāo)2
* @param segmentLength 分段長度
* @param deltaLength 增量長度
* @return 中間坐標(biāo)
*/
private static Coordinate buildMiddleCoordinate(Coordinate coordinate1, Coordinate coordinate2,
long segmentLength, long deltaLength) {
double deltaScale = deltaLength * 1.0D / segmentLength;
double middleX = round(coordinate1.x + (coordinate2.x - coordinate1.x) * deltaScale, DIGIT_SCALE);
double middleY = round(coordinate1.y + (coordinate2.y - coordinate1.y) * deltaScale, DIGIT_SCALE);
return new Coordinate(middleX, middleY);
}

/**
* 獲取目標(biāo)長度
*
* @param lineLength 線路長度
* @param totalLength 總共長度
* @param segmentLength 段長度
* @return 目標(biāo)長度
*/
private static long getTargetLength(double lineLength, double totalLength, double segmentLength) {
return Math.round(Math.max(segmentLength, 0.0D) * ZOOM_SCALE * lineLength / totalLength);
}

/**
* 四舍五入
*
* @param value 雙精度浮點(diǎn)值
* @param scale 保留小數(shù)位數(shù)
* @return 四舍五入值
*/
private static double round(double value, int scale) {
return BigDecimal.valueOf(value).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}

}

備注說明:

在超過目標(biāo)長度時(shí),獲取了一個(gè)中間坐標(biāo),由于數(shù)據(jù)精度的關(guān)系,這個(gè)坐標(biāo)可能跟上一坐標(biāo)或下一坐標(biāo)重合。這里為了降低這塊邏輯的復(fù)雜度,沒有進(jìn)行前后坐標(biāo)的去重處理。

存在問題:

在方法splitLineString(劃分線串)中,存在一個(gè)兩層循環(huán),導(dǎo)致了方法邏輯復(fù)雜、層級較深、代碼量大。如果把外層循環(huán)體提煉為一個(gè)方法,就能夠使代碼更簡潔、更清晰、更容易維護(hù)。

二 一次失敗的重構(gòu)經(jīng)歷

理論依據(jù):

當(dāng)看到一個(gè)方法定義過長或者這段方法需要很多注釋才能讓人理解的時(shí)候,這時(shí)候就要考慮是不是把這個(gè)方法的部分代碼提取出來,形成一個(gè)新的方法,方便調(diào)用和理解,同時(shí)也減小方法的粒度。我們把這種方法叫做提煉函數(shù)(Extract Function),在Java語言中可叫做提煉方法(Extract Method)。

重構(gòu)步驟:

  • 創(chuàng)建一個(gè)新方法,并根據(jù)這個(gè)方法的意圖來命名;
  • 將待提煉的代碼段從原方法中拷貝到新方法中;
  • 檢查提煉的代碼段,把缺少的變量添加到方法的參數(shù)中;
  • 如果部分參數(shù)成對出現(xiàn),可以把這些參數(shù)合并為一個(gè)參數(shù);
  • 如果方法需要有返回值,確定返回值的類型,并在合適的位置返回;
  • 在原方法中,刪除被提煉的代碼段,替換為新方法的調(diào)用。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {

/** 原有靜態(tài)常量 */
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 原有計(jì)算邏輯
......

// 初始化參數(shù)值
int index = 1;
Coordinate[] coordinates = lineString.getCoordinates();
Coordinate coordinate = coordinates[0];
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
lineStrings[i] = combineLineString(coordinates, index, coordinate, targetLengthes[i]);
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, index, coordinate);

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 組裝線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param index 當(dāng)前序號
* @param coordinate 當(dāng)前坐標(biāo)
* @param targetLength 目標(biāo)長度
* @return 線串
*/
private static LineString combineLineString(Coordinate[] coordinates, int index, Coordinate coordinate, long targetLength) {
// 添加線串坐標(biāo)
long addupLength = 0L;
List<Coordinate> coordinateList = new ArrayList<>();
coordinateList.add(coordinate);
for (; index < coordinates.length; index++) {
// 計(jì)算分段長度
long segmentLength = Math.round(coordinate.distance(coordinates[index]) * ZOOM_SCALE);

// 根據(jù)長度處理
boolean isBreak = true;
int compareResult = Long.compare(addupLength + segmentLength, targetLength);
// 根據(jù)長度處理: 未達(dá)目標(biāo)長度
if (compareResult < 0) {
addupLength += segmentLength;
coordinate = coordinates[index];
coordinateList.add(coordinate);
isBreak = false;
}
// 根據(jù)長度處理: 超過目標(biāo)長度
else if (compareResult > 0) {
long deltaLength = targetLength - addupLength;
coordinate = buildMiddleCoordinate(coordinate, coordinates[index], segmentLength, deltaLength);
}
// 根據(jù)長度處理: 等于目標(biāo)長度
else {
index++;
coordinate = coordinates[index];
}

// 是否跳出循環(huán)
if (isBreak) {
break;
}
}
coordinateList.add(coordinate);

// 返回線串對象
return buildLineString(coordinateList);
}

/** 原有靜態(tài)方法 */
......

}

存在問題:

粗看這段代碼,似乎沒有什么問題。但是,通過測試發(fā)現(xiàn),并沒有得到正確的結(jié)果。

分析原因:

在《Thinking in Java》中有這樣一段話:

When you’re passing primitives into a method,you get a distinct copy of the primitive. When you’re passing a reference into a method, you get a copy of the reference.

當(dāng)您將基本類型傳遞到方法中時(shí),您將得到該基本類型的副本。當(dāng)您將對象引用傳遞到方法中時(shí),您將得到該對象引用的副本。

原來參數(shù)index(當(dāng)前序號)和coordinate(當(dāng)前坐標(biāo))在方法combineLineString(組裝線串)中的修改,只是對該方法中的參數(shù)副本進(jìn)行修改,并沒有體現(xiàn)到調(diào)用方法splitLineString(劃分線串)中,從而導(dǎo)致每次調(diào)用都在使用參數(shù)的初始化值。其實(shí),這是在提取方法的過程中,沒有考慮到參數(shù)的作用域。

檢查技巧:

這里給出一個(gè)作者屢試不爽的檢查技巧——把提取方法的所有參數(shù)添加上final關(guān)鍵字,編譯后觀察到哪個(gè)參數(shù)出現(xiàn)編譯錯(cuò)誤,就說明這個(gè)參數(shù)是一個(gè)輸入輸出參數(shù)(Inout Parameter)。

解決方案:

在Java語言中,沒有直接的輸入輸出參數(shù)機(jī)制,無法簡單地實(shí)現(xiàn)參數(shù)的輸入輸出功能。所以,需要借助其它解決方案,來實(shí)現(xiàn)參數(shù)的輸入輸出功能。在這里,作者通過實(shí)踐總結(jié),給出了以下幾種解決方案。

三 利用方法參數(shù)實(shí)現(xiàn)

本章將從方法參數(shù)入手,實(shí)現(xiàn)參數(shù)的輸入輸出功能。

3.1 利用參數(shù)類實(shí)現(xiàn)

理論依據(jù):

引入?yún)?shù)對象(Introduce Parameter Object):當(dāng)一個(gè)方法的參數(shù)超過3個(gè)時(shí),就可以考慮將參數(shù)封裝成一個(gè)對象類。將參數(shù)封裝成對象類后,提高了代碼的可讀性,并且該參數(shù)對象類也可以重用。以后,如果增加或刪除參數(shù),方法本身不需要修改,只需要修改參數(shù)對象類就可以了。

這里,可以利用引入?yún)?shù)對象重構(gòu)方法,定義一個(gè)輸入輸出參數(shù)類,來實(shí)現(xiàn)參數(shù)的輸入輸出功能。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {

/** 原有靜態(tài)常量 */
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 原有計(jì)算邏輯
......

// 初始化參數(shù)值
Coordinate[] coordinates = lineString.getCoordinates();
InoutParameter inoutParameter = new InoutParameter(1, coordinates[0]);
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
lineStrings[i] = combineLineString(coordinates, inoutParameter, targetLengthes[i]);
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, inoutParameter.getIndex(), inoutParameter.getCoordinate());

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 組裝線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param inoutParameter 輸入輸出參數(shù)
* @param targetLength 目標(biāo)長度
* @return 線串
*/
private static LineString combineLineString(Coordinate[] coordinates, InoutParameter inoutParameter, long targetLength) {
// 獲取輸入?yún)?shù)
int index = inoutParameter.getIndex();
Coordinate coordinate = inoutParameter.getCoordinate();

// 添加線串坐標(biāo)
......

// 設(shè)置輸出參數(shù)
inoutParameter.setIndex(index);
inoutParameter.setCoordinate(coordinate);

// 返回線串對象
return buildLineString(coordinateList);
}

/** 原有靜態(tài)方法 */
......

/**
* 輸入輸出參數(shù)類
*/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
private static class InoutParameter {
/** 當(dāng)前序號 */
private int index;
/** 當(dāng)前坐標(biāo) */
private Coordinate coordinate;
}

}

3.2 利用單值數(shù)組實(shí)現(xiàn)

理論依據(jù):

當(dāng)您將對象引用傳遞到方法中時(shí),您將得到該對象引用的副本。也就是說,當(dāng)您將數(shù)組引用傳遞到方法中時(shí),您將得到該數(shù)組引用的副本。但是,這兩個(gè)數(shù)組引用都指向同一個(gè)數(shù)組,當(dāng)修改副本數(shù)組引用中的值時(shí),也能體現(xiàn)到原有數(shù)組引用中。

利用數(shù)組引用的這個(gè)特性,可以實(shí)現(xiàn)參數(shù)的輸入輸出功能。這里,引入了單值數(shù)組的概念,即一個(gè)數(shù)組只有一個(gè)值,用于傳遞輸入輸出參數(shù)值。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {

/** 原有靜態(tài)常量 */
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 原有計(jì)算邏輯
......

// 初始化參數(shù)值
int[] indexHolder = new int[] {1};
Coordinate[] coordinates = lineString.getCoordinates();
Coordinate[] coordinateHolder = new Coordinate[] {coordinates[0]};
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
lineStrings[i] = combineLineString(coordinates, indexHolder, coordinateHolder, targetLengthes[i]);
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, indexHolder[0], coordinateHolder[0]);

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 組裝線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param indexHolder 序號支撐
* @param coordinateHolder 坐標(biāo)支撐
* @param targetLength 目標(biāo)長度
* @return 線串
*/
private static LineString combineLineString(Coordinate[] coordinates, int[] indexHolder, Coordinate[] coordinateHolder, long targetLength) {
// 獲取支撐取值
int index = indexHolder[0];
Coordinate coordinate = coordinateHolder[0];

// 添加線串坐標(biāo)
......

// 設(shè)置支撐取值
indexHolder[0] = index;
coordinateHolder[0] = coordinate;

// 返回線串對象
return buildLineString(coordinateList);
}

/** 原有靜態(tài)方法 */
......

}

3.3 利用元組類實(shí)現(xiàn)

理論依據(jù):

元組(Tuple):Java中的元組(Tuple)是一種數(shù)據(jù)結(jié)構(gòu),可以存放多個(gè)元素,并且每個(gè)元素的數(shù)據(jù)類型可以不同。Tuple與List類似,但是不同的是,List只能存儲一種數(shù)據(jù)類型,而Tuple可存儲多種數(shù)據(jù)類型。

可能你會質(zhì)疑,Object類型的List實(shí)際也是可以存儲多種類型的啊?但是,在創(chuàng)建List時(shí),需要指定元素?cái)?shù)據(jù)類型,只能指定為Object類型;在獲取的元素時(shí),只能獲取到Object類型的值,需要強(qiáng)制轉(zhuǎn)化為對應(yīng)的數(shù)據(jù)類型。而Tuple在創(chuàng)建時(shí),可以直接指定多個(gè)元素?cái)?shù)據(jù)類型;在獲取元素時(shí),無需進(jìn)行數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)化。

常用的元組工具包有:

  • Apache的commons-lang3提供的元組類:
  • Pair
  • Triple
  • JavaTuples提供的元組類:

??

??


隨著元組的元數(shù)不斷地增加,代碼的閱讀性也逐漸地下降。當(dāng)元組的元數(shù)超過3個(gè)時(shí),不如直接創(chuàng)建對象類,并給予合適類名和字段名,便于代碼的理解和維護(hù)。所以,不建議使用JavaTuples中的元組類,而推薦使用Apache的commons-lang3提供的元組類。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {

/** 原有靜態(tài)常量 */
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 原有計(jì)算邏輯
......

// 初始化參數(shù)值
Coordinate[] coordinates = lineString.getCoordinates();
MutablePair<Integer, Coordinate> mutablePair = MutablePair.of(1, coordinates[0]);
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
lineStrings[i] = combineLineString(coordinates, mutablePair, targetLengthes[i]);
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, mutablePair.getLeft(), mutablePair.getRight());

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 組裝線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param mutablePair 當(dāng)前配對
* @param targetLength 目標(biāo)長度
* @return 線串
*/
private static LineString combineLineString(Coordinate[] coordinates, MutablePair<Integer, Coordinate> mutablePair,
long targetLength) {
// 獲取配對取值
int index = mutablePair.getLeft();
Coordinate coordinate = mutablePair.getRight();

// 添加線串坐標(biāo)
......

// 設(shè)置配對取值
mutablePair.setLeft(index);
mutablePair.setRight(coordinate);

// 返回線串對象
return buildLineString(coordinateList);
}

/** 原有靜態(tài)方法 */
......

}

3.4 利用支撐類實(shí)現(xiàn)

理論依據(jù):

在上一節(jié)里,把所有輸入輸出參數(shù)放入到一個(gè)元組里,每一個(gè)輸入輸出參數(shù)沒有一個(gè)具體的命名,造成了代碼的理解和維護(hù)困難。如果每一個(gè)輸入輸出參數(shù)都定義一個(gè)元組,可以讓代碼維護(hù)者輕松地知道每一個(gè)參數(shù)的具體含義。所以,這里定義了自己的一元元組類——ObjectHolder(對象支撐類,也可以使用javatuples的Unit類),用于傳遞輸入輸出參數(shù)值。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {
/** 原有靜態(tài)常量 */
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 原有計(jì)算邏輯
......

// 初始化參數(shù)值
Coordinate[] coordinates = lineString.getCoordinates();
ObjectHolder<Integer> indexHolder = new ObjectHolder<>(1);
ObjectHolder<Coordinate> coordinateHolder = new ObjectHolder<>(coordinates[0]);
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
lineStrings[i] = combineLineString(coordinates, indexHolder, coordinateHolder, targetLengthes[i]);
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, indexHolder.getValue(), coordinateHolder.getValue());

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 組裝線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param indexHolder 序號支撐
* @param coordinateHolder 坐標(biāo)支撐
* @param targetLength 目標(biāo)長度
* @return 線串
*/
private static LineString combineLineString(Coordinate[] coordinates, ObjectHolder<Integer> indexHolder, ObjectHolder<Coordinate> coordinateHolder, long targetLength) {
// 獲取支撐取值
int index = indexHolder.getValue();
Coordinate coordinate = coordinateHolder.getValue();

// 添加線串坐標(biāo)
......

// 設(shè)置支撐取值
indexHolder.setValue(index);
coordinateHolder.setValue(coordinate);

// 返回線串對象
return buildLineString(coordinateList);
}

/** 原有靜態(tài)方法 */
......

}

/**
* 對象支撐類
*/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class ObjectHolder<T> {

/** 對象取值 */
private T value;

}

3.5 利用其它方法實(shí)現(xiàn)

除此之外,還可以利用其它參數(shù)方法實(shí)現(xiàn)參數(shù)的輸入輸出功能:

利用數(shù)組實(shí)現(xiàn)

首先,在調(diào)用函數(shù)中,定義一個(gè)對象數(shù)組,把所有輸入輸出參數(shù)存入對象數(shù)組中;其次,在被調(diào)用函數(shù)中,把這些參數(shù)從對象數(shù)組中取出來使用;再次,在被調(diào)用函數(shù)中,再把這些參數(shù)值存入對象數(shù)組中;最后,在調(diào)用函數(shù)中,把這些參數(shù)值從對象數(shù)組中取出來使用。

利用對象數(shù)組的問題是——代碼可讀性太差,而且在參數(shù)的存入和取出過程中,需要進(jìn)行數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)化。如果所有輸入輸出參數(shù)的類型一致,可以直接定義該類型的數(shù)組,從而避免了數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)化。

利用Map實(shí)現(xiàn)

首先,在調(diào)用函數(shù)中,定義一個(gè)對象Map,把所有輸入輸出參數(shù)存入對象Map中;其次,在被調(diào)用函數(shù)中,把這些參數(shù)從對象Map中取出來使用;再次,在被調(diào)用函數(shù)中,再把這些參數(shù)值存入對象Map中;最后,在調(diào)用函數(shù)中,把這些參數(shù)值從對象Map中取出來使用。

利用對象Map實(shí)現(xiàn),代碼的可讀性比利用對象數(shù)組實(shí)現(xiàn)更強(qiáng),但是也存在同樣的問題——在參數(shù)的存入和取出過程中,需要進(jìn)行數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)化。如果所有輸入輸出參數(shù)的類型一致,可以直接定義該類型的Map,從而避免了數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)化。但是,利用對象Map實(shí)現(xiàn),還不如定義一個(gè)參數(shù)類更實(shí)用。

利用原子類實(shí)現(xiàn)

JDK中,提供了一套原子類——AtomicInteger、AtomicLong、AtomicDouble等,可用于對應(yīng)的基礎(chǔ)類型和包裝類型,實(shí)現(xiàn)對應(yīng)參數(shù)的輸入輸出功能。實(shí)現(xiàn)方法跟ObjectHolder一樣,這里不再累述。

四 利用方法返回值實(shí)現(xiàn)

本章將從方法返回值入手,實(shí)現(xiàn)參數(shù)的輸入輸出功能。

4.1 利用結(jié)果類實(shí)現(xiàn)

理論依據(jù):

引入返回值對象(Introduce Return Object):當(dāng)一個(gè)方法的需要返回多個(gè)值時(shí),就可以考慮將返回值封裝成一個(gè)對象類。將返回值封裝成對象類后,提高了代碼的可讀性,并且該返回值對象類也可以重用。以后,如果增加或刪除返回值,方法本身不需要修改,只需要修改返回值對象類就可以了。

這里,可以利用引入返回值對象重構(gòu)方法,定義一個(gè)返回值對象類,來實(shí)現(xiàn)參數(shù)的輸入輸出功能。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {

/** 原有靜態(tài)常量 */
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 原有計(jì)算邏輯
......

// 初始化參數(shù)值
int index = 1;
Coordinate[] coordinates = lineString.getCoordinates();
Coordinate coordinate = coordinates[0];
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
ReturnResult result = combineLineString(coordinates, index, coordinate, targetLengthes[i]);
index = result.getIndex();
coordinate = result.getCoordinate();
lineStrings[i] = result.getLineString();
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, index, coordinate);

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 組裝線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param index 當(dāng)前序號
* @param coordinate 當(dāng)前坐標(biāo)
* @param targetLength 目標(biāo)長度
* @return 返回值
*/
private static ReturnResult combineLineString(Coordinate[] coordinates, int index, Coordinate coordinate, long targetLength) {
// 添加線串坐標(biāo)
......

// 返回輸出結(jié)果
return new ReturnResult(index, coordinate, buildLineString(coordinateList));
}

/** 原有靜態(tài)方法 */
......

/**
* 返回值類
*/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
private static class ReturnResult {
/** 當(dāng)前序號 */
private int index;
/** 當(dāng)前坐標(biāo) */
private Coordinate coordinate;
/** 線串對象 */
private LineString lineString;
}

}

4.2 利用元組類實(shí)現(xiàn)

理論依據(jù):

參考3.3章節(jié)的元組(Tuple)的定義和特性。元組(Tuple)可以用于方法的參數(shù)值,也可以用于方法的返回值。當(dāng)一個(gè)方法需要返回多個(gè)值時(shí),又不愿意定義自己的結(jié)果類時(shí),可以采用元組(Tuple)實(shí)現(xiàn)多個(gè)值的返回。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {

/** 原有靜態(tài)常量 */
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 原有計(jì)算邏輯
......

// 初始化參數(shù)值
int index = 1;
Coordinate[] coordinates = lineString.getCoordinates();
Coordinate coordinate = coordinates[0];
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
Triple<Integer, Coordinate, LineString> triple = combineLineString(coordinates, index, coordinate, targetLengthes[i]);
index = triple.getLeft();
coordinate = triple.getMiddle();
lineStrings[i] = triple.getRight();
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, index, coordinate);

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 組裝線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param index 當(dāng)前序號
* @param coordinate 當(dāng)前坐標(biāo)
* @param targetLength 目標(biāo)長度
* @return 返回值
*/
private static Triple<Integer, Coordinate, LineString> combineLineString(Coordinate[] coordinates, int index, Coordinate coordinate, long targetLength) {
// 添加線串坐標(biāo)
......

// 返回輸出結(jié)果
return ImmutableTriple.of(index, coordinate, buildLineString(coordinateList));
}

/** 原有靜態(tài)方法 */
......

}

4.3 利用其它方法實(shí)現(xiàn)

除此之外,還可以利用其它返回值方法實(shí)現(xiàn)參數(shù)的輸入輸出功能:

利用數(shù)組實(shí)現(xiàn)

首先,在被調(diào)用方法中,定義一個(gè)對象數(shù)組,把多個(gè)返回值放入到對象數(shù)組中;最后,在調(diào)用函數(shù)中,把這些參數(shù)值從對象數(shù)組中取出來,并強(qiáng)制轉(zhuǎn)化為對應(yīng)的數(shù)據(jù)類型。

利用對象數(shù)組的問題是——代碼可讀性太差,而且在返回值的存入和取出過程中,需要進(jìn)行數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)化。如果所有返回值的數(shù)據(jù)類型一致,可以直接定義該類型的數(shù)組,從而避免了數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)化。

利用Map實(shí)現(xiàn)

首先,在被調(diào)用方法中,定義一個(gè)對象Map,把多個(gè)返回值放入到對象Map中;最后,在調(diào)用函數(shù)中,把這些參數(shù)值從對象Map中取出來,并強(qiáng)制轉(zhuǎn)化為對應(yīng)的數(shù)據(jù)類型。

利用對象Map實(shí)現(xiàn),代碼的可讀性比利用對象數(shù)組實(shí)現(xiàn)更強(qiáng),但是也存在同樣的問題——在返回值的存入和取出過程中,需要進(jìn)行數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)化。如果所有返回值的類型一致,可以直接定義該類型的Map,從而避免了數(shù)據(jù)類型的強(qiáng)制轉(zhuǎn)化。但是,利用對象Map實(shí)現(xiàn),還不如定義一個(gè)返回值類更實(shí)用。

五 利用類字段實(shí)現(xiàn)

本章將從類字段入手,實(shí)現(xiàn)參數(shù)的輸入輸出功能。

5.1 利用線程本地變量實(shí)現(xiàn)

理論依據(jù):

線程本地變量(ThreadLocal):線程本地變量不同于它們的普通變量,因?yàn)樵L問某個(gè)變量的每個(gè)線程都有自己的局部變量,且獨(dú)立于變量的初始化副本。線程本地變量實(shí)例通常是類中的私有靜態(tài)字段,它希望將變量狀態(tài)與某一個(gè)線程關(guān)聯(lián)起來。

要用類字段解決參數(shù)的輸入輸出問題,就必須考慮方法的線程安全性。這里,利用線程本地變量(ThreadLocal)來實(shí)現(xiàn)線程中輸入輸出參數(shù)值共享。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {

/** 屬性相關(guān) */
/** 當(dāng)前序號支撐 */
private static final ThreadLocal<Integer> INDEX_HOLDER = new ThreadLocal<>();
/** 當(dāng)前坐標(biāo)支撐 */
private static final ThreadLocal<Coordinate> COORDINATE_HOLDER = new ThreadLocal<>();

/** 原有靜態(tài)常量 */
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 原有計(jì)算邏輯
......

// 初始化參數(shù)值
INDEX_HOLDER.set(1);
Coordinate[] coordinates = lineString.getCoordinates();
COORDINATE_HOLDER.set(coordinates[0]);
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
lineStrings[i] = combineLineString(coordinates, targetLengthes[i]);
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, INDEX_HOLDER.get(), COORDINATE_HOLDER.get());

// 清除支撐類
INDEX_HOLDER.remove();
COORDINATE_HOLDER.remove();

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 組裝線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param targetLength 目標(biāo)長度
* @return 線串
*/
private static LineString combineLineString(Coordinate[] coordinates, long targetLength) {
// 獲取支撐取值
int index = INDEX_HOLDER.get();
Coordinate coordinate = COORDINATE_HOLDER.get();

// 添加線串坐標(biāo)
......

// 設(shè)置支持取值
INDEX_HOLDER.set(index);
COORDINATE_HOLDER.set(coordinate);

// 返回線串對象
return buildLineString(coordinateList);
}

/** 原有靜態(tài)方法 */
......

}

5.2 利用類成員變量實(shí)現(xiàn)

理論依據(jù):

在上一章節(jié)中,利用線程本地變量(ThreadLocal)來實(shí)現(xiàn)線程中輸入輸出參數(shù)值共享,讓方法的封裝更復(fù)雜——需要從線程本地變量(ThreadLocal)讀取和存儲輸入輸出參數(shù)值。有沒有一種更簡單的方法,直接利用類成員變量實(shí)現(xiàn)輸入輸出參數(shù)值的共享呢?

答案是肯定的,可以把方法的封裝和變量的定義封裝到一個(gè)類中。這樣,在每一個(gè)類實(shí)例中,都可以利用類成員變量來實(shí)現(xiàn)輸入輸出參數(shù)值的共享。但是,這個(gè)類是線程非安全的,必須在單線程中使用。

代碼實(shí)現(xiàn):

/** 
* 幾何輔助類
*/
public final class GeometryHelper {

// 原有構(gòu)造方法
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public static LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
SplitLineStringAlgorithm algorithm = new SplitLineStringAlgorithm();
return algorithm.splitLineString(lineString, segmentLengthes);
}

}

/**
* 劃分線串算法類
*/
public class SplitLineStringAlgorithm {

/** 屬性相關(guān) */
/** 當(dāng)前序號 */
private int index;
/** 當(dāng)前坐標(biāo) */
private Coordinate coordinate;

/** 原有靜態(tài)常量 */
......

/**
* 劃分線串
*
* @param lineString 原始線串
* @param segmentLengthes 分段長度數(shù)組
* @return 線串?dāng)?shù)組
*/
public LineString[] splitLineString(LineString lineString, double[] segmentLengthes) {
// 原有計(jì)算邏輯
......

// 初始化參數(shù)值
index = 1;
Coordinate[] coordinates = lineString.getCoordinates();
coordinate = coordinates[0];
int length = targetLengthes.length;
LineString[] lineStrings = new LineString[length];

// 添加前面N段
for (int i = 0; i < length - 1; i++) {
lineStrings[i] = combineLineString(coordinates, targetLengthes[i]);
}

// 添加最后一段
lineStrings[length - 1] = buildLineString(coordinates, index, coordinate);

// 返回線串?dāng)?shù)組
return lineStrings;
}

/**
* 組裝線串
*
* @param coordinates 坐標(biāo)數(shù)組
* @param targetLength 目標(biāo)長度
* @return 線串
*/
private LineString combineLineString(Coordinate[] coordinates, long targetLength) {
// 添加線串坐標(biāo)
......

// 返回線串對象
return buildLineString(coordinateList);
}

/** 原有靜態(tài)方法 */
......

}

六 各種方法綜合點(diǎn)評

下面,針對以上各種實(shí)現(xiàn)方法進(jìn)行一個(gè)綜合點(diǎn)評:

??

??

 

總結(jié)如下:

各種實(shí)現(xiàn)方法有利有弊,應(yīng)當(dāng)根據(jù)具體的使用場景,來選擇最適合的實(shí)現(xiàn)方法。

根據(jù)參數(shù)和返回值的類型選擇實(shí)現(xiàn)方法:輸入輸出參數(shù)盡量使用方法參數(shù)實(shí)現(xiàn),返回值盡量使用返回值實(shí)現(xiàn)。

根據(jù)參數(shù)和返回值的數(shù)量選擇實(shí)現(xiàn)方法:數(shù)量少的盡量使用支撐類和元組類,數(shù)量多的盡量使用自定義類。

不建議使用一些取巧的實(shí)現(xiàn)方法,比如:3.2.利用單值數(shù)組實(shí)現(xiàn)、5.1.利用線程本地變量實(shí)現(xiàn)。

不推薦使用對象數(shù)組和對象Map,Java是強(qiáng)類型定義語言,不建議使用強(qiáng)制數(shù)據(jù)類型轉(zhuǎn)化。

 

最適合本文中案例的實(shí)現(xiàn)方法是——3.4.利用支撐類實(shí)現(xiàn)。

 

 

后記

《莊子·養(yǎng)生主》有言:

吾生也有涯,而知也無涯。以有涯隨無涯,殆已!

意思是:我的生命是有限的,但知識卻是無限的。用有限的生命去追求無限的知識,必然會失敗的。

所以,知識并不是越多越好,而是“學(xué)而精之,精而深之,深而新之 ”。

【本文為51CTO專欄作者“阿里巴巴官方技術(shù)”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】

??戳這里,看該作者更多好文??

 

責(zé)任編輯:武曉燕 來源: 51CTO專欄
相關(guān)推薦

2011-11-28 09:25:36

Java輸入輸出

2010-02-06 14:28:38

C++標(biāo)準(zhǔn)輸入輸出

2010-02-03 15:35:00

C++輸入輸出漢字

2009-12-17 11:36:55

Ruby輸入輸出

2021-05-07 20:01:23

IO輸入輸出

2021-07-27 12:55:14

Java數(shù)據(jù)應(yīng)用程序

2009-12-23 10:57:20

nohup命令

2014-09-04 11:39:43

Linux

2011-07-11 11:05:09

Windows控制臺

2021-06-24 10:24:57

Linux輸入輸出設(shè)備Interface

2021-12-01 11:40:14

Python 輸入輸出

2024-09-09 06:00:00

Python輸入編程

2023-10-30 08:53:36

Python輸入輸出

2021-03-31 12:41:24

C語言編程語言

2009-01-18 11:14:06

軟設(shè)計(jì)算機(jī)系統(tǒng)知識I

2013-12-12 17:21:46

Lua出入輸出

2009-07-30 11:43:32

2009-12-15 17:48:20

Ruby輸入輸出

2021-04-12 15:34:33

C語言printfscanf

2025-02-24 11:03:11

Python編程語言數(shù)據(jù)類型轉(zhuǎn)換
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 亚洲欧美成人影院 | 久久高清精品 | 欧美精三区欧美精三区 | 亚洲人成人一区二区在线观看 | 九九九视频精品 | 成人av网站在线观看 | 欧美日产国产成人免费图片 | av片免费观看 | 国产精品一二区 | 日韩久久精品电影 | 精品一区二区在线视频 | 亚洲高清一区二区三区 | 蜜臀久久99精品久久久久久宅男 | 日本精品久久 | 在线观看中文视频 | 少妇诱惑av| 国内自拍视频在线观看 | 婷婷色国产偷v国产偷v小说 | 色偷偷噜噜噜亚洲男人 | 国产成都精品91一区二区三 | 91亚洲国产 | 久在线视频播放免费视频 | 日本高清中文字幕 | 欧美视频xxx | 羞羞视频免费观看 | 99精品国自产在线观看 | 精品欧美一区二区三区精品久久 | 久久一区二区三区四区 | 国产偷自视频区视频 | 久久久久亚洲 | 久久久久久久国产精品 | 美日韩免费视频 | 一区精品国产欧美在线 | www.蜜桃av.com| 亚洲激情综合 | 久草精品视频 | 欧美日韩亚洲一区 | 亚洲一区高清 | 日韩av在线不卡 | 成人小视频在线观看 | 亚洲美女av网站 |