我一直在读这的好书,我决定在第5章中完成这个项目:为Hack汇编语言编写一个汇编程序。
您可以找到Hack Machine语言和Hack汇编语言这里的规范。
我在这里有一个工作实现,已经。那个花了我2-3个小时。然后,我想按照最佳实践编写代码。这次我花了5-6个小时。我怀疑哪一个更易读。但是,在这个版本中,任何评论都是受欢迎的。我的目标是尽我所能地把所有的东西都解除耦合,将代码编码到接口中,而不是具体的类。
我必须说,这两种实现所做的事情是相同的,但这一种的长度大概是我的5-6倍。也许更多..。它充满了类、包、接口等。
这真的遵循了最佳实践吗?你看到依赖问题了吗?我应该多上几节课吗?更模棱两可?
package biz.tugay.hack.assembler.assemble;
/* User: koray@tugay.biz Date: 10/03/15 Time: 15:56 */
import java.io.IOException;
public interface InstructionFileAssembler {
public void assembleFile() throws IOException;
}
package biz.tugay.hack.assembler.assemble;
/* User: koray@tugay.biz Date: 09/03/15 Time: 22:20 */
import biz.tugay.hack.assembler.helperutils.commentRemover.InstructionCommentRemover;
import biz.tugay.hack.assembler.instruction.Instruction;
import biz.tugay.hack.assembler.instruction.factory.InstructionFactory;
import biz.tugay.hack.assembler.instruction.instructionConverter.InstructionToBinaryConverter;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Scanner;
public class InstructionFileAssemblerImpl implements InstructionFileAssembler {
private InstructionCommentRemover instructionCommentRemover;
private InputStream inputStream;
private InstructionFactory instructionFactory;
private List<InstructionToBinaryConverter> instructionToBinaryConverters;
public InstructionFileAssemblerImpl(InstructionCommentRemover instructionCommentRemover,
InputStream inputStream,
InstructionFactory instructionFactory,
List<InstructionToBinaryConverter> instructionToBinaryConverters) {
this.instructionCommentRemover = instructionCommentRemover;
this.inputStream = inputStream;
this.instructionToBinaryConverters = instructionToBinaryConverters;
this.instructionFactory = instructionFactory;
}
@Override
public void assembleFile() throws IOException {
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNextLine()) {
String convert = convert(scanner.nextLine());
if(convert != null && convert.length() != 0) {
System.out.println(convert);
}
}
}
private String convert(String instructionToConvert) {
// Obtain the next instruction from the input stream.
// If the instruction contains comments, remove them and trim the instruction
if (instructionToConvert.contains(instructionCommentRemover.getCommentRule())) {
instructionToConvert = instructionCommentRemover.removeCommentFromSingleInstruction(instructionToConvert);
}
// Build the instruction from using the provided factory.
Instruction instruction = instructionFactory.buildInstruction(instructionToConvert);
// If we have a valid instruction..
if (instruction != null && instruction.getMnemonic() != null && instruction.getMnemonic().length() != 0) {
return doConvert(instruction);
}
return null;
}
private String doConvert(Instruction instruction) {
for (InstructionToBinaryConverter instructionToBinaryConverter : instructionToBinaryConverters)
if (instruction.getInstructionType().equals(instructionToBinaryConverter.getInstructionType()))
return instructionToBinaryConverter.getBinaryRepresentationForInstruction(instruction);
throw new UnsupportedOperationException();
}
}
package biz.tugay.hack.assembler.helperutils.commentRemover;
/* User: koray@tugay.biz Date: 10/03/15 Time: 14:47 */
public interface InstructionCommentRemover {
/**
* Removes the comment part of a single line instuction.
* The implementing class must hardcode the comment rule.
* @param instruction : Single instruction line.
* @return Returns the instruction without the comment part.
*/
public String removeCommentFromSingleInstruction(String instruction);
public String getCommentRule();
}
package biz.tugay.hack.assembler.helperutils.commentRemover;
/* User: koray@tugay.biz Date: 10/03/15 Time: 14:47 */
public class InstructionRemoverImpl implements InstructionCommentRemover {
private String commentRule = "//";
@Override
public String removeCommentFromSingleInstruction(String instruction) {
if (instruction.contains(commentRule)) {
instruction = instruction.split(commentRule)[0];
}
return instruction.trim();
}
@Override
public String getCommentRule() {
return commentRule;
}
}
package biz.tugay.hack.assembler.helperutils;
/* User: koray@tugay.biz Date: 09/03/15 Time: 20:25 */
public class BinaryFormatter {
public static String appendZerosToString(String stringToBeAppended) {
char[] chars = stringToBeAppended.toCharArray();
int zerosToAppend = 16 - chars.length;
StringBuilder stringBuilder = new StringBuilder();
for(int i=0;i<zerosToAppend;i++) {
stringBuilder.append("0");
}
stringBuilder.append(stringToBeAppended);
return stringBuilder.toString();
}
}
package biz.tugay.hack.assembler.instruction.factory;
import biz.tugay.hack.assembler.instruction.HackInstructionTypeA;
import biz.tugay.hack.assembler.instruction.HackInstructionTypeC;
import biz.tugay.hack.assembler.instruction.Instruction;
/* User: koray@tugay.biz Date: 09/03/15 Time: 22:23 */
public class HackInstructionFactory implements InstructionFactory {
@Override
public Instruction buildInstruction(String instruction) {
Instruction hackInstruction;
if(instruction.startsWith("(")) {
return null;
}
if(instruction.startsWith("@")) {
hackInstruction = new HackInstructionTypeA(instruction);
}
else {
hackInstruction = new HackInstructionTypeC(instruction);
}
return hackInstruction;
}
}
package biz.tugay.hack.assembler.instruction.factory;
/* User: koray@tugay.biz Date: 10/03/15 Time: 15:52 */
import biz.tugay.hack.assembler.instruction.Instruction;
/**
* An Instruction Factory implementation should return a concrete and correct @Instruction object
*/
public interface InstructionFactory {
/**
* The implementing method must return one of the Instructions that the
* Assembly Language implements.
* @param instruction The code to be used to find the matching Instruction object and creating it.
* @return Instruction object.
*/
Instruction buildInstruction(String instruction);
}
package biz.tugay.hack.assembler.instruction.instructionConverter;
/* User: koray@tugay.biz Date: 09/03/15 Time: 21:10 */
import biz.tugay.hack.assembler.instruction.Instruction;
import biz.tugay.hack.assembler.instruction.InstructionType;
import java.util.Map;
public interface InstructionToBinaryConverter {
public String getBinaryRepresentationForInstruction(Instruction instruction);
public InstructionType getInstructionType();
public void addToLookUpMap(Map<String, Integer> map);
}
package biz.tugay.hack.assembler.instruction.instructionConverter;
/* User: koray@tugay.biz Date: 09/03/15 Time: 21:27 */
import biz.tugay.hack.assembler.helperutils.BinaryFormatter;
import biz.tugay.hack.assembler.instruction.Instruction;
import biz.tugay.hack.assembler.instruction.InstructionType;
import java.util.HashMap;
import java.util.Map;
public class InstructionToBinaryConverterInstructionAImpl implements InstructionToBinaryConverter {
private int memoryLocationCounter = 16;
private Map<String,Integer> lookUpMap = new HashMap<String, Integer>();
private InstructionType instructionType = InstructionType.A_INSTRUCTION;
{
lookUpMap.put("SP" ,0);
lookUpMap.put("LCL" ,1);
lookUpMap.put("ARG" ,2);
lookUpMap.put("THIS" ,3);
lookUpMap.put("THAT" ,4);
lookUpMap.put("R0" ,0);
lookUpMap.put("R1" ,1);
lookUpMap.put("R2" ,2);
lookUpMap.put("R3" ,3);
lookUpMap.put("R4" ,4);
lookUpMap.put("R5" ,5);
lookUpMap.put("R6" ,6);
lookUpMap.put("R7" ,7);
lookUpMap.put("R8" ,8);
lookUpMap.put("R9" ,9);
lookUpMap.put("R10" ,10);
lookUpMap.put("R11" ,11);
lookUpMap.put("R12" ,12);
lookUpMap.put("R13" ,13);
lookUpMap.put("R14" ,14);
lookUpMap.put("R15" ,15);
lookUpMap.put("SCREEN" ,16384);
lookUpMap.put("KBD" ,24576);
}
@Override
public String getBinaryRepresentationForInstruction(Instruction instruction) {
String mnemonic = instruction.getMnemonic();
String symbol = mnemonic.substring(1);
boolean isDirectAddress = true;
try {
Integer.parseInt(symbol);
} catch (NumberFormatException e) {
isDirectAddress = false;
}
if(isDirectAddress) {
return BinaryFormatter.appendZerosToString(Integer.toBinaryString(Integer.valueOf(symbol)));
}
if(mnemonic.startsWith("@")) {
if(lookUpMap.containsKey(symbol)) {
return BinaryFormatter.appendZerosToString(Integer.toBinaryString(lookUpMap.get(symbol)));
}
if(!lookUpMap.containsKey(symbol)) {
lookUpMap.put(symbol,memoryLocationCounter);
memoryLocationCounter++;
}
if(lookUpMap.containsKey(symbol)) {
return BinaryFormatter.appendZerosToString(Integer.toBinaryString(lookUpMap.get(symbol)));
}
}
throw new UnsupportedOperationException();
}
public InstructionType getInstructionType() {
return instructionType;
}
@Override
public void addToLookUpMap(Map<String, Integer> map) {
lookUpMap.putAll(map);
}
}
package biz.tugay.hack.assembler.instruction.instructionConverter;
/* User: koray@tugay.biz Date: 09/03/15 Time: 22:10 */
import biz.tugay.hack.assembler.instruction.InstructionType;
import biz.tugay.hack.assembler.instruction.Instruction;
import java.util.HashMap;
import java.util.Map;
public class InstructionToBinaryConverterInstructionCImpl implements InstructionToBinaryConverter {
private Map<String,String> lookUpMap = new HashMap<String, String>();
private Map<String,String> compMap = new HashMap<String, String>();
@Override
public String getBinaryRepresentationForInstruction(Instruction instruction) {
final String mnemonic = instruction.getMnemonic();
// We definetly have compBinary in the instruction...
String compBinaryRepresentation = getBinaryRepresentationForCompMnemnonic(mnemonic);
String jumpMnemonic = "null";
String jumpBinaryRepresentation;
if (mnemonic.contains(";")) {
jumpMnemonic = mnemonic.split(";")[mnemonic.split(";").length-1];
}
jumpBinaryRepresentation = lookUpMap.get(jumpMnemonic);
String destMnemonic = "null";
String destinationBinaryRepresentation;
if(mnemonic.contains("=")) {
destMnemonic = mnemonic.split("=")[0];
}
destinationBinaryRepresentation = lookUpMap.get(destMnemonic);
return "111" + compBinaryRepresentation + destinationBinaryRepresentation + jumpBinaryRepresentation;
}
@Override
public InstructionType getInstructionType() {
return InstructionType.C_INSTRUCTION;
}
private String getBinaryRepresentationForCompMnemnonic(String mnemonic) {
String compMnemonnic = mnemonic.split(";")[0];
compMnemonnic = compMnemonnic.split("=")[compMnemonnic.split("=").length-1];
return compMap.get(compMnemonnic);
}
{
compMap.put("0" ,"0101010");
compMap.put("1" ,"0111111");
compMap.put("-1" ,"0111010");
compMap.put("D" ,"0001100");
compMap.put("A" ,"0110000");
compMap.put("!D" ,"0001101");
compMap.put("!A" ,"0110001");
compMap.put("-D" ,"0001111");
compMap.put("-A" ,"0110011");
compMap.put("D+1","0011111");
compMap.put("A+1","0110111");
compMap.put("D-1","0001110");
compMap.put("A-1","0110010");
compMap.put("D+A","0000010");
compMap.put("D-A","0010011");
compMap.put("A-D","0000111");
compMap.put("D&A","0000000");
compMap.put("D|A","0010101");
compMap.put("M" ,"1110000");
compMap.put("!M" ,"1110001");
compMap.put("-M" ,"1110011");
compMap.put("M+1","1110111");
compMap.put("M-1","1110010");
compMap.put("D+M","1000010");
compMap.put("D-M","1010011");
compMap.put("M-D","1000111");
compMap.put("D&M","1000000");
compMap.put("D|M","1010101");
}
{
lookUpMap.put("null","000");
lookUpMap.put("JGT" ,"001");
lookUpMap.put("JEQ" ,"010");
lookUpMap.put("JGE" ,"011");
lookUpMap.put("JLT" ,"100");
lookUpMap.put("JNE" ,"101");
lookUpMap.put("JLE" ,"110");
lookUpMap.put("JMP" ,"111");
}
{
lookUpMap.put("null","000");
lookUpMap.put("M" ,"001");
lookUpMap.put("D" ,"010");
lookUpMap.put("MD" ,"011");
lookUpMap.put("A" ,"100");
lookUpMap.put("AM" ,"101");
lookUpMap.put("AD" ,"110");
lookUpMap.put("AMD" ,"111");
}
@Override
public void addToLookUpMap(Map<String, Integer> map) {
throw new UnsupportedOperationException("You can not mess with this instruction type!");
}
}
package biz.tugay.hack.assembler.instruction;
/* User: koray@tugay.biz Date: 09/03/15 Time: 21:01 */
public class HackInstructionTypeA implements Instruction {
private String mnemonic;
public HackInstructionTypeA(String instruction) {
this.mnemonic = instruction;
}
@Override
public InstructionType getInstructionType() {
return InstructionType.A_INSTRUCTION;
}
@Override
public String getMnemonic() {
return mnemonic;
}
}
package biz.tugay.hack.assembler.instruction;
/* User: koray@tugay.biz Date: 09/03/15 Time: 22:08 */
public class HackInstructionTypeC implements Instruction {
private String mnemonic;
public HackInstructionTypeC(String instruction) {
this.mnemonic = instruction;
}
@Override
public InstructionType getInstructionType() {
return InstructionType.C_INSTRUCTION;
}
@Override
public String getMnemonic() {
return mnemonic;
}
}
package biz.tugay.hack.assembler.instruction;
/* User: koray@tugay.biz Date: 10/03/15 Time: 15:51 */
public interface Instruction {
InstructionType getInstructionType();
String getMnemonic();
}
package biz.tugay.hack.assembler.instruction;
/* User: koray@tugay.biz Date: 09/03/15 Time: 20:52 */
public enum InstructionType {
A_INSTRUCTION,C_INSTRUCTION;
}
package biz.tugay.hack.assembler.label;
/* User: koray@tugay.biz Date: 10/03/15 Time: 14:45 */
import java.util.Map;
public interface LabelMapBuilder {
public Map<String, Integer> buildLabelMapForGivenFile();
}
package biz.tugay.hack.assembler.label;
/* User: koray@tugay.biz Date: 09/03/15 Time: 22:36 */
import biz.tugay.hack.assembler.helperutils.commentRemover.InstructionCommentRemover;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class LabelMapBuilderImpl implements LabelMapBuilder {
private Character labelStartCharacter;
private InstructionCommentRemover instructionCommentRemover;
private InputStream inputStream;
public LabelMapBuilderImpl(InputStream inputStream,
InstructionCommentRemover instructionCommentRemover,
Character labelStartCharacter) {
this.inputStream = inputStream;
this.instructionCommentRemover = instructionCommentRemover;
this.labelStartCharacter = labelStartCharacter;
}
@Override
public Map<String, Integer> buildLabelMapForGivenFile() {
int lineCounter = 0;
Map<String, Integer> labelMap = new HashMap<String, Integer>();
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNextLine()) {
String instruction = scanner.nextLine();
if(instruction.contains(instructionCommentRemover.getCommentRule())) {
instruction = instructionCommentRemover.removeCommentFromSingleInstruction(instruction);
}
if (isInstructionLabel(instruction)) {
addInstructionToLabelMap(lineCounter, labelMap, instruction);
lineCounter--;
}
lineCounter++;
}
return labelMap;
}
private void addInstructionToLabelMap(int lineCounter, Map<String, Integer> labelMap, String instructionWithoutComment) {
labelMap.put(instructionWithoutComment.substring(1, instructionWithoutComment.length() - 1), lineCounter);
}
private boolean isInstructionLabel(String instruction) {
return instruction.startsWith(Character.toString(labelStartCharacter));
}
}最后是测试类:
package biz.tugay.hack.assembler;
/* User: koray@tugay.biz Date: 10/03/15 Time: 16:15 */
import biz.tugay.hack.assembler.assemble.InstructionFileAssembler;
import biz.tugay.hack.assembler.assemble.InstructionFileAssemblerImpl;
import biz.tugay.hack.assembler.helperutils.commentRemover.InstructionCommentRemover;
import biz.tugay.hack.assembler.helperutils.commentRemover.InstructionRemoverImpl;
import biz.tugay.hack.assembler.instruction.factory.HackInstructionFactory;
import biz.tugay.hack.assembler.instruction.factory.InstructionFactory;
import biz.tugay.hack.assembler.instruction.instructionConverter.InstructionToBinaryConverter;
import biz.tugay.hack.assembler.instruction.instructionConverter.InstructionToBinaryConverterInstructionAImpl;
import biz.tugay.hack.assembler.instruction.instructionConverter.InstructionToBinaryConverterInstructionCImpl;
import biz.tugay.hack.assembler.label.LabelMapBuilder;
import biz.tugay.hack.assembler.label.LabelMapBuilderImpl;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class TestClass {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream(new File(args[0]));
InstructionCommentRemover instructionCommentRemover = new InstructionRemoverImpl();
LabelMapBuilder labelMapBuilder = new LabelMapBuilderImpl(inputStream,instructionCommentRemover,'(');
Map<String, Integer> labelMap = labelMapBuilder.buildLabelMapForGivenFile();
InstructionToBinaryConverter instructionToBinaryConverterInstructionA = new InstructionToBinaryConverterInstructionAImpl();
instructionToBinaryConverterInstructionA.addToLookUpMap(labelMap);
InstructionToBinaryConverter instructionToBinaryConverterInstructionC = new InstructionToBinaryConverterInstructionCImpl();
List<InstructionToBinaryConverter> instructionToBinaryConverterList = new ArrayList<InstructionToBinaryConverter>();
instructionToBinaryConverterList.add(instructionToBinaryConverterInstructionA);
instructionToBinaryConverterList.add(instructionToBinaryConverterInstructionC);
InstructionFactory factory = new HackInstructionFactory();
inputStream = new FileInputStream(new File(args[0]));
InstructionFileAssembler instructionFileAssembler =
new InstructionFileAssemblerImpl(instructionCommentRemover,inputStream,factory,instructionToBinaryConverterList);
instructionFileAssembler.assembleFile();
}
}发布于 2015-03-11 19:20:46
指出明显的间接层(接口、策略类等)。只有当你从他们提供的灵活性中受益的时候,你才会变得“更好”。否则,就是过度工程。
但是,让我们假设您的汇编程序将针对两种不同的CPU架构。您的代码架构良好吗?嗯,我不能回答这个问题,因为我不知道第二个建筑是什么样子。我不知道它与第一章有什么不同,代码的哪些部分需要灵活来支持它。在最坏的情况下,它可能必须直接实现InstructionFileAssembler,在这种情况下,您过早创建的所有其他扩展点实际上都将不使用。在类似的不确定性情况下,程序员的真正目标是编写易于以后抽象但暂时简单而优雅的代码。想象一下可能的设计模式,但不要实际实现它们。
不过我已经够难的了。
InstructionFileAssembler和其他只实现一次的接口。(但是将类的公共方法保持为通用,就好像它们实现了一个接口。)InstructionFileAssembler Assembler即可。所有汇编程序都处理指令文件。别做明显的队长。名字必须简明扼要。InstructionCommentRemover应该被称为Preprocessor,将来除了删除注释之外,它可能还会做一些事情。当发生这种情况时,预处理器可能会调用一个类CommentRemover。没有所谓的“指令性评论”。String convert = convert(scanner.nextLine());看起来应该更像String machineCode = assemble (scanner.nextLine());。变量名应该是名词。方法名称应该是特定的谓词。instructionToConvert应该是instruction,除非您的代码块中有其他指令。if (instructionToConvert.contains(instructionCommentRemover.getCommentRule())) instructionToConvert = instructionCommentRemover.removeCommentFromSingleInstruction(instructionToConvert);是一种代码气味,有几个原因。在最坏的情况下,应该是if (commentRemover.containsComment (instruction)) instruction = commentRemover.process (instruction)。最好是简单的instruction = commentRemover.process (instruction)。但是最好的方法是让预处理器在将其传递给汇编程序之前对整个程序集文件执行它的工作。Instruction应该知道如何对自己进行编码(并公开toMachineCode())。毕竟,这就是OOP的目标。在内部,它可以使用方法Architecture调用类encode(Instruction)。InstructionFactory.buildInstruction替换Instruction.valueOf,但是您也可以有一个InstructionParser.parseHackInstructionFactory不应返回无法编码的null或格式错误的指令。看看空对象模式。发布于 2015-03-10 21:31:05
BinaryFormatter需要很少或根本不需要解释。InstructionToBinaryConverterInstructionAImpl. getBinaryRepresentationForInstruction可以使用一些解释。超长名称描述了使用该方法的预期结果,但它没有提到它是如何完成任务的。看起来,您可以通过//之类的东西将指令转换为二进制形式,这取决于//指令类型。如果指令是符号,且符号不//已知,则将符号添加到本地HashMap字段lookUpMap。@重写公共字符串representInBinary(指令指令){}https://codereview.stackexchange.com/questions/83775
复制相似问题