クラス設計的な視点で。
ちっちゃいツールなんだから、細けーこと気にせんわ、ということなら、読み飛ばしてください。
com.amazonaws.services.s3 なパッケージの import があちこちに散らばってるのが気になります。
ぼくなら、Task か Uploader に、Amazon の都合を押し込めます。
どっち(もしくは、両方)に押し込めるかは、ちょっと悩みます。
Task は、Callable の実装が欲しいだけなので、Uploader の inner class にして、call メソッドでは Uploader のメソッドを呼ぶ、という形にするかも。
同じ理屈で、CLI は、あくまでもパラメータの解釈とチェックだけにとどめる。
UploadableFile は、java.io.File の気持ち悪さを引きずってます(個人的感想です)。
java.io.File が、OS が提供する「ファイル」の現身だとしたら、remove() とか renameTo() とかが、ものすごく気持ち悪く感じます。インスタンスとしての実体があるのに「消えろ」とかいうメッセージが。
File f = ...;
FileSystem fs = ...;
fs.remove(f);
みたいな感じだったら、分かるんですけど。
なので、UploadableFile の upload や getS3Key メソッドが気持ち悪くって。
ぼくなら、Uploader で実装します。
「Amazon S3 の仕様が変わりました」とか「他のストレージサイトにも対応したい」というときに、何をどこまで変更しなきゃいけないのか、というところで効いてくるはずです。