使用 AI 爲開發提速

與 AI 同行。

出題

最直接使用 AI 的方法,就是出題。出題方式適用於寫工具類。

如 “再談函數式編程:釋放編程創造力” 一文所示。


再給一例:

AI + 函數式 + 泛型編程,將能讓你的編程效率成倍提升。

package util

import (
    "runtime"
    "sync"
)

/*
 * 接收一個普通函數 plainFunc,返回一個可以併發調用 plainFunc 的函數,這個函數接收一個列表,返回另一個列表
 * 這個結果列表由單個函數調用結果彙集而成
 */
func ConcurrentExecute[T any, R any](plainFunc func([]T) []R) func([]T) []R {
    return func(list []T) []R {
        ch := make(chan []R)
        var wg sync.WaitGroup

        subListSize := len(list) / runtime.NumCPU()
        if subListSize == 0 {
            subListSize = 1
        }

        for i := 0; i < len(list); i += subListSize {
            end := i + subListSize
            if end > len(list) {
                end = len(list)
            }
            wg.Add(1)

            go func(start, end int) {
                defer wg.Done()
                ch <- plainFunc(list[start:end])
            }(i, end)
        }

        go func() {
            wg.Wait()
            close(ch)
        }()

        var finalResult []R
        for resultPart := range ch {
            finalResult = append(finalResult, resultPart...)
        }

        return finalResult
    }
}

利用 AI 生成自動化工具

更復雜的出題,讓 AI 生成自動化的完整實用工具。注意要詳述要求。

比如,java 工程轉 Go,需要把大量 Java 對象轉成 Go 結構體。需要一個批量轉換工具,不是手工編碼。

寫一個 java 程序,對於給定 java 對象,將其轉成 Go 結構體。

比如


public class ScriptDTO {

    @JsonProperty("pguid")
    private String pguid;

    /**
     * 腳本類型
     */
    @JsonProperty("type")
    private String type;

   @JsonProperty("rule")
    private Rule rule;

    @JsonProperty("sha256s")
    private List<String> sha256s;

     @JsonProperty("param")
    private Map<String, Object> param;

    @JsonProperty("create_time")
    private Long createTime;

    
   
}

轉成

type AgentScript struct {

    Pguid string `json:"pguid"`
    Type string `json:"type"`
    Rule Rule `json:"rule"`
    CreateTime int64 `json:"create_time"`
    Sha256s []string `json:"sha256s"`
    Param map[string]any `json:"param"`

}

要求滿足如下條件:

  1. 類名、字段名、字段類型可以反射方式獲取
  2. Java 字段類型要轉換爲 go 的對應字段類型
  3. go 結構體字段與 Java 類定義的字段順序保持一致。
  4. Java 註釋內容 /** comment */ 寫在 結構體字段定義的後面,以 // 開始;如果沒有則省略。
  5. 考慮字段沒有 @JsonProperty("rule") 註解的空情形。
  6. 要求能夠處理 java 裏的各種原子類型,比如 int, Integer, String, Boolean, Long, long; 自定義類型按原樣輸出。
  7. go 結構體字段要首字母大寫。
  8. 將 go 結構體寫入用戶主目錄下 ~/ 的文件。文件名是類名的小寫下劃線形式,去掉最前面的下劃線。
  9. 保持程序的模塊化和易修改,做好錯誤處理。
  10. 請給出完整可用程序,不要僞代碼。

寫一個 java 完整程序,指定一個 java 包名,對這個包及其子包下的所有 後綴爲 DTO.java ,或 DO.java 的 java 類,根據上述方法生成對應 go 結構體,並寫入對應文件,寫入用戶主目錄下 ~/ 的文件。文件名是類名的小寫下劃線形式,去掉最前面的下劃線。 結構層級與包層級保持一致。

有時,AI 不能一次性生成完整程序,則需要出多道題把答案拼起來。

固定模式代碼

比如設計模式代碼。指定設計模式名稱,則可直接生成對應代碼。

例如:“Go 模板:用代碼生成代碼”一文中用 AI 生成生成器模式代碼。

類似還有工廠、策略模式代碼等。

提供樣例

如果要生成的代碼比較多但有固定樣式,則可以提供樣例。如下所示:

請記住如下樣例:

給定一個 java 枚舉:

public enum DetectionMethodEnum {

    PROCESS_HASH("process_hash", "進程Hash檢測"),

    private final String type;
    private final String desc;

}

生成對應的 go 枚舉爲:

type DetectionMethod string

type DetectionMethodInfo struct {
    MethodType string
    Desc       string
}

const (
    ProcessHash  DetectionMethod = "PROCESS_HASH"
)

var DetectionMethodMap = map[DetectionMethod]DetectionMethodInfo{
    ProcessHash: {
        MethodType: "process_hash",
        Desc:       "進程Hash檢測"
    }
}

請嚴格按照樣例,爲下面的 java 枚舉生成 go 枚舉。

@Getter
@AllArgsConstructor
public enum ContainerTypeEnum {

    DOCKER("docker", "docker_container"),
    CONTAINERD("cri", "cri_containerd"),
    CRIO("crio", "crio_container");

    private final String type;
    private final String name;

}


提供模板

與提供樣例類似。模板通過若干變量生成。

請記住如下樣例:

給定 do 類 DetectionDO, 生成:

type DetectionService interface {
	service.BaseService[*models.DetectionDO]
}

type DetectionServiceImpl struct {
	service.BaseService[*models.DetectionDO]
}

func NewDetectionService(repo repository.DetectionRepository) DetectionService {
	return &DetectionServiceImpl{
		BaseService: service.NewMongoService[*models.DetectionDO](repo),
	}
}

func init() {
	dow.ProvideGlobal[DetectionService](nil, func(injector *do.Injector) (DetectionService, error) {
		repo, err := dow.InvokeGlobal[repository.DetectionRepository](nil)
		if err != nil {
			return nil, err
		}
		return NewDetectionService(repo), nil
	})
}
type DetectionRepository interface {
	repository.BaseRepository[*models.DetectionDO]
}

type DetectionRepositoryImpl struct {
	repository.BaseRepository[*models.DetectionDO]
}

func NewDetectionRepository(db *mongox.Database) DetectionRepository {
	return &DetectionRepositoryImpl{
		BaseRepository: repository.NewMongoRepository[*models.DetectionDO](db.Collection(models.DetectionDO{}.TableName())),
	}
}

func init() {
	dow.ProvideGlobal[DetectionRepository](nil, func(injector *do.Injector) (DetectionRepository, error) {
		database, err := mongox.TenantDatabase()
		if err != nil {
			return nil, err
		}
		return NewDetectionRepository(database), nil
	})
}

請爲 DO 類 IdsFileUploadTaskDO 生成 service 和 repository 代碼。

語言翻譯

將如下 Java 代碼轉換爲 Go 代碼:

public static String[] getCmdOutput(String cmd) throws InterruptedException, IOException {
        long start = System.currentTimeMillis();
        Process process = new ProcessBuilder().command("sh", "-c", cmd).start();
        boolean ret = process.waitFor(3, TimeUnit.SECONDS);
        String output = StringUtils.EMPTY;
        String error = IOUtils.toString(process.getErrorStream(), StandardCharsets.UTF_8);
        if (ret) {
            output = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8);
            LOG.info("cmd: {} output: {}", cmd, output);
        } else {
            LOG.warn("cmd:{} ret is false", cmd);
            process.destroyForcibly();
        }
        long end = System.currentTimeMillis();
        LOG.info("exec cmd: {} cost: {} ms", cmd, (end-start));
        return new String[] { output, error };
    }

分離純函數

很多業務方法,大部分都是純邏輯(輸入相同則輸出也相同,具有引用透明性),只有少量是依賴外部服務的。

這時候,可以運用“分離純函數”的技巧,將方法中的純邏輯與依賴分離。AI 特別適合於生成無語境或無依賴的純函數。

可參考 “改善代碼可測性的若干技巧”和“使用Java函數接口及lambda表達式隔離和模擬外部依賴更容易滴單測”這兩篇文章。

具體方法是: 先對方法進行拆解,把外部依賴部分抽取出來。外部依賴部分可以用函數參數替代,這樣這個方法就變成了一個純函數,可以用 AI 自動生成,並用 AI 生成測試用例和單測。

仔細再審視我們的工程代碼,會發現,其實 80% 都是純邏輯,真正的業務部分可能只佔 20%。經過良好抽象之後,把純邏輯提取出來,把業務部分做成配置,則理論上 80% 的代碼可以用 AI 生成。

AI 代碼糾錯

定義

type ValueModifier[T any] func(T, ...any)

func DuplicateAndModifyElement[T any](list []*T, keyFunc funcs.IdKey[T], mFunc funcs.ValueModifier[*T], args ...any) []*T {
    seen := make(map[string]bool)
    result := make([]*T, 0, len(list))

    for _, elem := range list {
        key := keyFunc(*elem)
        if !seen[key] {
            seen[key] = true
            mFunc(elem, args)
            result = append(result, elem)
        }
    }

    return result
}

爲什麼 這行報錯,如何修復

pb := []*test.PersonBasicInfo{{Name: "qin", Age: 36}, {Name: "ni", Age: 28}, {Name: "qin", Age: 29}, {Name: "ni", Age: 28}}
 
undupAndModified := util.DuplicateAndModifyElement(pb, func(info test.PersonBasicInfo) string { return info.Name }, func(p *test.PersonBasicInfo, skills ...string) { p.Skills = skills }, "programming")

AI CodeReview

現在 gitlab 已經集成了 AI Code review 。這些 review 還是相對中肯的。


小結

對於高級程序員來說, AI 是一個強大的工具,真如虎添翼。

如何利用 AI 來提升效率和創造性,是一個很好的話題。

更多姿勢還在解鎖中。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章