我正在研究的项目要求我写出大量重复的代码。例如,如果我想在我的代码中加载名为“logo.png”的图像文件,我会写这样的东西: 位图logoImage;
...
// Init
logoImage = load("logo.png")
...
// Usage
logoImage.draw(0, 0);
..
// Cleanup
logoImage.release();
必须编写此代码才能使用每个新图像,这很麻烦,包括必须指定logoImage应加载文件“logo.png”。
由于我正在研究Java Android游戏,并且图像在内部循环中被大量使用,我真的想避免像制作虚拟函数调用那样缓慢的事情。我可以避免它访问数组/地图/对象字段。从Android API(生成的R类)复制一个想法,我想我可以在编译之前运行一个实用程序来为我生成一些重复的代码。例如,项目文件中的实际代码将简化为:
logoImage.draw(0, 0);
使用一些命令行工具(例如grep,sed),我可以查找“Image.draw(...”的每个实例,然后自动生成其他所需的代码,即加载/释放文件的代码.png并在某处声明“Bitmap logoImage”。这段代码可以添加到新类中,也可以在我的代码中添加占位符,告诉代码生成器在哪里插入生成的代码。
要显示新图像,我需要做的只是将图像文件复制到正确的目录并添加一行代码。很好,很简单。这避免了创建图像数组,定义标记的int常量以引用数组以及必须指定要加载的文件名等方法。
这是一个非常糟糕的主意吗?这看起来有点像黑客,但我看不到更简单的方法,它似乎彻底清理我的代码。是否有任何标准工具用于执行这种简单的代码生成(即该工具不需要理解代码的含义)?还有其他人做这样的事情来弥补语言特征吗?
答案 0 :(得分:5)
使用代码生成这样的事情是个坏主意。 (IMO,代码生成应保留用于需要生成大量代码的情况,这听起来不像那种情况。)
如果当前解决方案中的样板代码与您有关,那么更好的解决方案(代码生成)是实现图像注册表抽象; e.g。
public class ImageRegistry {
private Map<String, Image> map = new HashMap<String, Image>();
public synchronized Image getImage(String filename) {
Image image = map.get(filename);
if (image == null) {
image = load(filename);
map.put(filename, image);
}
return image;
}
public synchronized void shutdown() {
for (Image image : map.valueSet()) {
image.release();
}
map.clear(); // probably redundant ...
}
}
将logoImage.draw(0, 0)
等替换为:
registry.getImage("logo.png").draw(0, 0);
删除所有load
个来电,并通过一次调用release
来替换所有registry.shutdown()
个来电。
编辑以回应OP的评论如下:
...但我提到我正在为手机写游戏。每次我绘制精灵时,HashMap查找都会导致性能下降。
啊......我从另一个线程中记得你。
您(再次)在实际绩效测量中没有任何依据的情况下对绩效做出假设。具体来说,您假设HashMap查找过于昂贵。我的直觉是,进行查找所花费的时间将是绘制图像所花费时间的一小部分(<10%)。此时,它正接近用户无法察觉的水平。
如果你的测量结果(或者直觉)告诉你一个hashmap查找过于昂贵,那么写一个简单的修改:
Image image = registry.getImage(“logo.png”); 而(...){ ... image.draw(0,0); }
例如,Google甚至建议您不要在内部循环中使用迭代器,因为这会导致在解除分配Iterator对象时触发GC。
这是无关紧要和不准确的。
使用String键进行HashMap查找不会产生垃圾。永远不会。
在内部循环中使用迭代器不会“导致在解除分配Iterator对象时触发GC”。在Java中,您不释放对象。这就是C / C ++的思考。
在内部循环中实例化迭代器会new
一个对象,但只有在new
操作达到内存阈值时才会触发GC。这种情况偶尔发生。
此外,编写“file_that_does_not_exist.png”不会被您的示例选为编译时错误。
原始解决方案或代码生成方法都不会为丢失的文件提供编译时错误。
答案 1 :(得分:2)
避免代码生成。它经常使代码难以维护。
在你的情况下,你为什么不这样做:
public class ImageUtils {
public static void drawAndRelease(String name) {
logoImage = load(name)
logoImage.draw(0, 0);
logoImage.release();
}
}
然后只需致电:
ImageUtils.drawAndRelease("logo.png");
如果这些方法之间有更多的代码 - 那么它们就是原子方法,如果你使用代码生成,你就不知道它们放在哪里。
答案 2 :(得分:1)
我是Bozho关于避免代码生成的第二个答案,但是如果你必须编写可重复的代码片段,那么任何好的IDE通常都会内置一些支持,用于指定带有变量和所有内容的自己的代码片段。 IntelliJ IDEA具有此功能,称为实时模板。我猜Eclipse和NetBeans都有类似的功能。
答案 3 :(得分:1)
您将复杂性转移到代码生成,而它(生成)并不是微不足道的,可能是错误的。 代码更难以阅读和维护。设计和编码的一些明确规则在这里更有帮助。
答案 4 :(得分:0)
有几种方法:
Eclipse code2code,你使用FreeMarker,groovy等模板语言编写模板
Android的eclipse sqLite插件自动生成sqlite代码
MotoDevStudio4android包含可以使用的代码片段