前言
还在把 AI 当作搜索引擎的替代品,有问题才问 AI,没问题就在那边吭哧吭哧地撸代码?假如是这样,那你真的 OUT了!现在正经人谁还自己一行行地写代码啊,都是 AI 生成的代码——没有 AI 我不写(手动诙谐)。
本文将分享 AI 时代的编程新实践,教你怎么从一个 “Ctrl + C”、 “Ctrl + V” 工程师,变成一个 “Tab + Enter” 工程师。
开发流程
软件的一般研制流程为:
- 需求剖析
- 程序规划
- 代码编写
- 软件测验
- 布置上线
咱们在这里主要关心步骤2~4,由于与 AI 结合得比较紧密。尽管需求剖析也能够借助 AI,但不是本文的重点,故不做评论。
程序规划
经过需求剖析、逻辑整理后,在编写实践代码前,需求进行程序规划。
此环节的产品是规划文档,是什么类型的规划文档不重要,重要的是伪代码的输出。
尽管《Code Complete》早就引荐过伪代码的实践,但对此人们简单有一个误区:以为写伪代码花的时间,现已够把实践代码写好了。但 AIGC 时代,此问题能够轻松破解:AI 写代码的速度必定比人快,因而,只要能找到方法能让 AI 生成契合需求的代码,就值得花时间去研究。而伪代码,便是让 AI 快速生成契合希望的实践代码的最好方法。
为什么这么说呢?由于想要让 AIGC 契合希望,恰当的 Prompt 必不可少。但怎么写好这个 Prompt,需求提供多少上下文,才干让 AI 更好地了解咱们的目的,这是需求技巧、需求调试的。而经过精心规划的伪代码,本身现已提供了足够的上下文,且目的足够聚焦,减轻了对 Prompt 的要求,又提高了 AIGC 的成功率。
咱们来看一下伪代码示例:
plainText = JSON.stringify(data)
digest = hash(plainText) // 防篡改
secret = Symmetric.getKey()
cipherText = encryptText(data, secret) // 防内容泄密
pair = Asymmetric.getPair()
cipherSecret = encryptKey(secret, pair.getPrivateKey()) // 防密钥泄露
return {
digest,
cipherText,
cipherSecret,
pair.getPublicKey(),
}
上述代码主要表达的内容是:
- 对初始数据进行加密
- 回来加密后的相关内容
- 涉及到了一系列的算法
对于伪代码的编写,有如以建议:
- 不要纠结类型,不要局限于某一种编程言语的语法,不用试图写能编译经过的代码——这只会限制了你的表达
- 命名一定要精确,契合范畴术语,这一点很关键。这一点能够经过查找材料、看书、问 AI 来完结。千万不要只是僵硬地汉译英,不专业的表达会妨碍 AI 的了解
代码编写
代码编写分为以下几步:
- 把伪代码转换成目标言语可履行的代码
- 依据项目情况,对生成的代码进行改造
- 运用 AI 编码辅助东西编写剩余的代码
生成实在代码
让 AI 生成代码的 prompt 很简单,示例如下(依据目标言语进行修正):
please transform the following pseudo code into practical java code ```
your code```please transform the following pseudo code into practical java code ```
your code```
运用 chatGPT 成果截图:
完整代码如下:
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.*;
public class EncryptionExample {
public static void main(String[] args) throws Exception {
// Prepare the license data
LicenseData licenseData = new LicenseData();
licenseData.setLicenseId("123");
licenseData.setLicenseType("Standard");
// Convert the license data to JSON string
ObjectMapper objectMapper = new ObjectMapper();
String plainText = objectMapper.writeValueAsString(licenseData);
// Hash the plain text
String digest = hash(plainText);
// Generate a symmetric key
SecretKey secretKey = generateSymmetricKey();
// Encrypt the license data with the symmetric key
byte[] cipherText = encryptText(plainText, secretKey);
// Generate an asymmetric key pair
KeyPair keyPair = generateAsymmetricKeyPair();
// Encrypt the symmetric key with the private key
byte[] cipherSecret = encryptKey(secretKey.getEncoded(), keyPair.getPrivate());
// Print the results
System.out.println("Plain Text: " + plainText);
System.out.println("Digest: " + digest);
System.out.println("Cipher Text: " + new String(cipherText));
System.out.println("Cipher Secret: " + new String(cipherSecret));
}
public static String hash(String input) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes());
return bytesToHex(hash);
}
public static SecretKey generateSymmetricKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
return keyGenerator.generateKey();
}
public static byte[] encryptText(String data, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data.getBytes());
}
public static byte[] encryptKey(byte[] secret, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return cipher.doFinal(secret);
}
public static KeyPair generateAsymmetricKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
return keyPairGenerator.generateKeyPair();
}
public static String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}
public static class LicenseData {
private String licenseId;
private String licenseType;
public String getLicenseId() {
return licenseId;
}
public void setLicenseId(String licenseId) {
this.licenseId = licenseId;
}
public String getLicenseType() {
return licenseType;
}
public void setLicenseType(String licenseType) {
this.licenseType = licenseType;
}
}
}
把上述代码 copy 下来,放到工程中,依据需求改造即可。
这里特别要说下,强烈引荐运用原版 AI,而不是寻找平替,相同的 prompt,下图是某一平替输出的成果:
只生成了函数声明,没有生成函数完结。二者对比,未免相形见绌。
辅助编程东西
改造的过程中,少不了 AI pair programming tools。对此,我引荐运用 Amazon 的 CodeWhisperer,原因很简单,跟 GitHub Copilot 比较,它是免费的。
CodeWhisperer 的装置能够看文末的装置教程,咱们先来看一下它是怎么辅助咱们编码的。
第一种方法是最简单的,那便是什么都不论,等候智能提示即可,就好像 IDEA 本来的提示一样,只不过更智能。
下图示例中,要把本来的中文异常提示,修正成英文,而我只输入了两个字符 IM
, 就得到了智能提示,补全了完整的英文字符串!
能够注意到,上图的智能建议一共有 5 条,相应的快捷键为:
- 方向键 ->,查看下一条提示
- 方向键 <-,查看上一条提示
- Tab,采用该提示
- Esc,拒绝提示
咱们再来看第二种 CodeWhisperer 的运用方法,编写注释,取得编码建议。
最终一种便是编写一个空函数,让 CodeWhisperer 依据函数名去猜想函数的完结,这种情况需求足够的上下文,才干得到令人满意的成果。
软件测验
AI 生成的内容,并不是完全可信任的,因而,单元测验的重要性变得尤为突出。
对上述代码编写测验代码后,实践上并不能一次经过,由于前面 AI 生成的代码参数有误。
此时需求一边履行单测,一边依据成果与 AI 进行交互:
经过修正,最终测验用例经过!
总结
本文经过事例,展现了 AI 怎么结合软件研制的流程,提升咱们的编程效率的。
其中,个人以为最重要的是编写伪代码与进行单元测验。有趣的是,这两样实践在 AIGC 时代之前,就现已被以为是最佳实践。这给咱们启示:某些方法论、实践经得起时间的考验,技能更新迭代,它们历久弥新。
别的,AI 是否能进一步浸透咱们的工作流,还有待探究。此文作引抛砖引玉之用,期待我们的后续分享。
附:CodeWhisperer 装置
下载 2023 年的 IDEA,翻开 Plugins Marketplace,找到 AWS Toolkit
装置完结、重启 IDEA 后,点击左下角,按下图所示操作:
假如第一次运用,就点击 1 处进行注册,假如现已有账号了,就点击 2 处运用自己的账号登录。
注册、登录、授权成功后,出现如图所示页面,即可运用 CodeWhisperer。