プログラミング初心者が中・上級者になるための近道
公開日:
:
最終更新日:2014/09/01
ITエンジニア成功術
Contents
初心者と中級者、上級者の違いって何?
初心者と中級者、上級者の違いとは何でしょうか?
初心者は
・知識が少ない
・開発したソフトウェアの数が少ない
中級者・上級者はその逆で
・知識が多い
・開発したソフトウェアの数が多い
その結果生まれる実質的な差は、
「初心者は簡単なものしか作れないけど、中級者・上級者は難しいものを作れる」
ということでしょう!
というわけで、初心者が中上級者になるには難しいソフトウェアを作るのに役立つ知識を身につければいいわけです。
難しいソフトウェアとは
・ロジックが複雑で難しい
・規模が大きい
・性能要件が厳しい
・納期が短い
など、いろいろなむずかしさがあります。
今回はこれらに対抗するのに必要な知識・技術について見ていきたいと思います。
この記事を読みながら解説する動画もあるので良かったら見てください。記事読みながら動画をラジオ的に流しておくのもおすすめです。
規模が大きいソフトウェアを作るための技術
規模が大きいソフトウェアを作るための技術には以下のようなものがあります。
1. モジュール分割
2. アプリケーションアーキテクチャ
3. フレームワーク
4. プログラミング作法
5. リファクタリング
1. モジュール分割
規模が大きいソフトウェアを作るためにはモジュールを上手に分割する必要があります。モジュール分割とは簡単に言えば、ソースコードを役割毎に分けて整理することです。規模が大きいと、上手に分割整理しないとソースコードが管理不能になってしまいます。例えばすべてのソースコードを一つのディレクトリに入れた場合を想像すると、ソースコードを探すのが大変になることが容易に想像できます。
2. アプリケーションアーキテクチャ
そこで、モジュールごとにディレクトリを分ける必要があります。では、どのようにモジュール分割するかですが、アプリケーションアーキテクチャを設計すればモジュール分割の指針が生まれます。
例えば、MVCアーキテクチャでこのソフトウェアを作る!と決めたら、この時点でModel、View、Controllerの3つにモジュールを分けることが出来ます。その3つに分けた中で必要であれば、例えばModelの中で分類していけばソースコードを適切に分割整理することが出来ます。
3. フレームワーク
アプリケーションアーキテクチャを具体的に実装したものがフレームワークです。例えばRuby On RailsはMVCアーキテクチャを実装したフレームワークです。Ruby On Railsの規則に従ってソフトウェアを開発すれば、自然とMVCアーキテクチャによるモジュール分割が出来ます。
4. プログラミング作法
大規模なソフトウェアを開発するにはたくさんのプログラマーが必要です。プログラムの書き方はプログラマーによって千差万別です。100人プログラマーがいて100通りの書き方をされた場合、ばらばらの書き方がされてしまって、読みにくく、メンテナンスが大変です。そこで、ソースコードの書き方に対する指針が必要です。その指針となるものがプログラミング作法なのです。
そのものずばりなタイトルの「プログラミング作法」という書籍があります。C言語で有名なカーニハンとロブパイクの共著です。この本では良いソースコードの3原則として
・簡潔性
・明瞭性
・一般性
を挙げています。
「簡潔性」とはソースコードを出来るだけ短くことです。短く書くために重複したコードを共通化する必要があります。同じことを何度も書いたらソースコードが長くなってしまうからです。よって、共通化は必須です。コピペコードを書いていたら簡潔性を満たせません。
「明瞭性」とは、分かりやすさのことです。そのコードが何をしているかが一目でわかるように書かれているコードが明瞭な良いコードです。明瞭なコードを書くには、名前の付け方が重要です。変数名やメソッド名が適切な名前のコードは一目で何をやっているコードなのかが分かります。逆に名前の付け方が適切でない場合、いくら読んでも何をやっているコードなのかわからない、あるいは、誤読してしまうことになりがちです。
例1と例2を見比べてください。これらは同じ処理をしています。
例1
int moge = 0; for (int hoge = 0; hoge < array.length; hoge++) { moge += array[hoge]; }
例2
int sum = 0; for (int i = 0; i < prices.length; i++) { sum += prices[i]; }
例1だと名前の付け方が適切でない為、何をやっているコードなのか分かりにくいですが、例2のように適切な名前が付けられていれば一目で何をやっているかがわかります。
また、例2であればプログラムを読めば、何をやっているかが分かる、つまりコードが意図を語るコードとなっています。このようなコードにはコメントを書く必要がありません。コードが意図を語っているからです。
それに対して例1はコードが意図を語っていません。それを補うためにコメントを書いたとしても、それは解決にはなりません。コードが意図を語っているかを意識して書くことが大切です。
例えば、以下のようにコメントを書けば、何をやっているコードかをせつめいすることは出来ますが、コメントとコードがかけ離れているため誤解が生まれやすくなっています。
//価格の合計を計算する
int moge = 0; for (int hoge = 0; hoge < array.length; hoge++) { moge += array[hoge]; }
このようなコードを書くよりも以下のように書けばコメントがなくても意図が伝わります。
calculateSumPrices(prices); int calculateSumPrices(int[] prices) { int sum = 0; for (int i = 0; i < prices.length; i++) { sum += prices[i]; } }
コメントがなくてもコードが意図を語る明瞭なコードを書くことがいかに大事か分かりましたね!
「一般性」とは奇をてらったコードを書かずに一般的なコードを書くということです。
プログラマーの中には自分の能力の高さをアピールしたいがあまり、わざと難しいコードを書く人がいますが、それはプロジェクトにとってマイナスです。趣味で一人でコードを書く場合にはそれでもいいのですが、大人数で一つのソフトウェアを開発する場合、仕事が一人では完結せず他人が書いたコードを読む必要があり、その際に、いくら技術的に優れていても難しく多くの人が読み解けないコードはマイナスでしかありません。
なので、コードを書く際には、一般的にはどうやって書くかな?ということを意識して書くと良いでしょう。
一番意識しやすいのは名前の付け方です。例えばユーザーのEメールアドレスを格納する変数名を何にするかを考えてみます。
String emailAddress;
String email;
String eAddress;
String ema;
等、人によって変数名の付け方は変わりますが、どれが一番一般的で誤解がないものでしょうか?emailAddressかemailでしょう。emaのように単語を省略しすぎてしまうと、何の変数なのかがわからなくなってしまいます(本人を除いて)。また、省略の仕方も個人個人で差が生まれやすくなります。よって、単語を省略する際には注意が必要です。省略を絶対にしてはいけないというものではありません。あまりに長い識別子だとプログラムが読みにくくなってしまいます。変数名の長さはケースバイケースでバランスを考えたうえで一般的で誤解がない名前になっているかを意識すると良いでしょう!
このようなプログラミング作法について書かれた本としては「プログラミング作法」の他に「リーダブルコード」があります。こちらはGoogleエンジニアが書いた本です。プログラミング作法はもちろんいい本ですが、出版されてから10年以上経っており、内容が少々古く読みにくさがあるのですが、リーダブルコードはそれよりだいぶ後に書かれた本なので、読みやすい内容となっています。
5. リファクタリング
良いコードを意識して書くことは重要ですが、納期が迫っていて時間に余裕がない時には、良いコードを書くことよりも、早く完成させることが求められる場合があります。その場合はその場しのぎの汚いコードであっても早くコードを書いた方がいい時もあります。一旦その場しのぎの実装をしておいて、後で時間がある時にコードを分かりやすいものへと書き換えればいいわけです。これをリファクタリングと言います。
また、良いコードを心がけて書いたコードであっても、改修を積み重ねていく内に分かりにくいコードになってしまう場合は多々あります。よってリファクタリングはタイミングを見計らって定期的に行えるといいでしょう。
ロジックが複雑で難しいソフトウェアを作るための技術
ロジックが複雑で難しいソフトウェアを作るための技術として以下のようなものがあります。
1. 関心毎の分離
2. デバッガ
3. ログ出力
1. 関心毎の分離
人間が一度に考えられることには限界があります。複雑なロジックの場合、考えなければならないことが多すぎるので、それらを関心毎の単位で分離していく適切な大きさに分割することができます。
例えば、以下のようなユーザー登録のコードがあるとします。
public void doPost(HttpServletRequest request, HttpServletResponse response) { ArrayListerrors = new ArrayList (); String email = request.getParemeter("email"); String name = request.getParemeter("name"); if (email == null) { errors.add("Eメールアドレスを入力してください"); } else if (email.length > 100) { errors.add("Eメールアドレスは100文字以内にしてください"); } if (name == null) { errors.add("ユーザー名を入力してください"); } else if (name.length > 30) { errors.add("ユーザー名は30文字以内にしてください"); } if (errors.size() > 0) { request.setAttribute("errors", errors); return; } Connection con = ConnectionFactory.getConnection(); con.preparedStatment("INSERT INTO User(email, name) VALUES (?, ?)"); stmt.setString(name); stmt.setString( email); stmt.executeUpdate(); con.commit(); request.setAttribute("name", name); request.setAttribute("email", email); forward("/registerComplete"); }
このコードは関心事が分離されていません。
このコードは大きく分けて3種類に分かれます。
・入力チェック
・DBアクセス
・画面表示
それらが分離されず一体になっています。今回はメソッドに切り出して関心事を分離してみます。(クラスを分けるやり方もある)
public void doPost(HttpServletRequest request, HttpServletResponse response) { String email = request.getParemeter("email"); String name = request.getParemeter("name"); ArrayListerrors = checkInput(email, name); if (errors.size() > 0) { request.setAttribute("errors", errors); return; } registerUser(email, name); fowardPage(request, email, name); } private ArrayList checkInput(String email, String name) { if (email == null) { errors.add("Eメールアドレスを入力してください"); } else if (email.length > 100) { errors.add("Eメールアドレスは100文字以内にしてください"); } if (name == null) { errors.add("ユーザー名を入力してください"); } else if (name.length > 30) { errors.add("ユーザー名は30文字以内にしてください"); } return errors; } private void registerUser(String email, String name) { Connection con = ConnectionFactory.getConnection(); con.preparedStatment("INSERT INTO User(email, name) VALUES (?, ?)"); stmt.setString(name); stmt.setString( email); stmt.executeUpdate(); con.commit(); } private fowardPage(HttpServletRequest request, String email, String name) { request.setAttribute("name", name); request.setAttribute("email", email); forward("/registerComplete"); }
このように関心事を分離すると、ソースコードが読みやすく理解しやすくなるし、実装する時にも分離した小さい単位のロジックに集中できるため、コードを書きやすくなります。複雑な難解なロジックも関心毎の単位に分離していけば、最終的に小さな簡単なロジックの集まりとなります。そこまで分離していけば、後はその一つ一つの小さくて簡単なロジックを実装していけばいいわけです。
2. デバッガ
プログラムを書いて動かしたら、思った通りに動かないということはよくあります。その場合、デバッガを使います。大抵のIDEにはデバッガがついていてソースコードの指定した位置にブレークポイントを張ることができます。ブレークポイントを張った状態でプログラムを実行するとブレークポイントを張ったコードの行でプログラムを一時停止することが出来ます。一時停止した状態でその時の変数の値を確認することが出来ます。
また、ブレークポイントで止めた位置から一行ずつコードを実行することも出来ます。なので、プログラムの挙動を一つ一つ確かめることが出来ます。一行ずつ実行することが出来ることはメリットでもありますが、時間が掛かります。例えば1万回のループを一行ずつステップ実行したらむちゃくちゃ時間が掛かってしまいます。そこで次に紹介するログ出力の手法が役に立ちます。
3. ログ出力
ログ出力とは変数の値を確認したい箇所にその変数を出力するログ出力命令を書くことによって、プログラムの挙動を確認する方法です。
例えばこんな感じのコードです。
元コード
for (int i = 0; i < results.length; i++) { ResultDto result = result[i]; if (result.price > 100000 && result.discount == true) { result.msg = "お買い得"; } }
ログ埋め込み後
for (int i = 0; i < results.length; i++) { ResultDto result = result[i]; if (result.price > 100000 && result.discount == true) { result.msg = "お買い得"; logger.info(i + "見目のデータ price=" + result.price); } }
ログ出力させているのはこのコードです。
logger.info(i + "見目のデータ price=" + result.price);
配列のインデックス番号とデータの中身を出力させています。
ログ出力のいいところは結果の確認が早いことです。この例でいうとresultsの個数が1万件あったとしてもプログラムの実行は一瞬です。実行後、出力されたログを確認すればいいわけです。実行も確認も早くできるのですが、デメリットもあります。それは、いちいちログ出力のコードを書かなければならないということです。どこにログ出力を入れてどの情報を出力させるかは自分で決めなければなりません。無意味な場所にログを仕込んでも問題は解決できません。なので、どこにログを仕込めばいいかが分からないくらい、コードの動きが把握できていない場合はデバッガを使った方が良いでしょう。また、見たい情報の項目が多い場合、ログにも出力項目をたくさん書かなければなりません。この場合もデバッガの方がいいでしょう。
まとめると以下のようになります。
デバッガ | ログ出力 |
---|---|
見たい情報が決まっていない | 見たい情報が決まっていり |
見たい情報の項目数が多い | 見たい情報の項目数が少ない |
多数ループの中は見ない | 多数ループの中を見たい |
状況に応じて使い分けるのがいいでしょう!
性能要件が厳しい
性能要件が厳しい場合に役立つ技術には以下のようなものがあります。
1. アルゴリズム
2. 並列化
1. アルゴリズム
性能の良さというのは2つの尺度があります。
A. 使用するメモリー量が少ない
B. 処理速度が速い
処理速度を測る尺度としてオーダーという概念があります。
オーダーとは解に達するまでに必要な操作回数のことを言います。
たとえばデータ探索のアルゴリズムの場合、前から順番に辿っていく線形探索の場合オーダーはNとなります。Nは要素の個数です。これは要素の個数が増えると計算量も比例して上がっていくので大量個数を処理する場合、計算量が
思いっきり上がってしまうため良い性能が出ません。それに対して二分探索の場合、O(log2n乗)なので、要素数が増えても計算量はそれほど上がりません。
アルゴリズムによってオーダーや使用するメモリー量の特性が分かるので、システムの性能要件に合ったアルゴリズムを選択すると良いでしょう!
2. 並列化
いくら速いアルゴリズムを選択したとしても、一台のコンピュータで出来ることは限られています。一台のコンピュータでは求められる性能が出せない場合も処理を分散して並列に処理することが出来れば、求められる性能を出すことが出来る場合があります。
また、最近のCPUはCPUコア一つにおける性能が限界に達していて、コアを複数にすることで性能向上させています。マルチコアCPUの性能を引き出す時にも処理をコアごとに分散して並列処理させる必要があります。よって、現代のプログラマーにとって、並列化は重要な課題です。
納期が短い
納期が短いソフトウェアを開発するには生産性の高める必要があります。
生産性を高める技術として以下のようなものがあります。
1. 抽象度の高いプログラミング言語
2. IDE
1. 抽象度の高いプログラミング言語
例えばある数値配列の中から値が4以上の要素を集めるという処理を書く場合(v7までの)Javaで書くと
int[] list = {4, 9, 3, 1, 5, 2, 7, 6}; ArrayListresults = new ArrayList (); for (int i in list) { if (i >= 4) { results.add(i); } }
となるのがRubyなら
list = [4, 9, 3, 1, 5, 2, 7, 6] results = list.select {|item| item >=4 }
で済みます。
Javaだと7行必要な処理がRubyだと2行で書けています。
Javaもv8ではストリームAPIというものが実装されてRubyに近い書き方が出来るようになりましたが、以前のJavaだとこれほど違いがありました。
言語によって、1行で書ける処理の量が大きく違います。少ない行数でたくさんのことが出来る言語を選んだ方が当然生産性は上がります。
一度覚えた言語をずっと使い続けて極めるのも良いですが、より良い言語はないかに関心を払って良いものを見つけたら、積極的に使っていくといいでしょう!
2. IDE
IDE(統合開発環境)とはエディタやコンパイラなどの開発に必要なものを統合し使いやすくして提供されたツールのことです。
有名なものだとEclipseやVisualStudioといったものがあります。
IDEに付属しているエディタを使うとコード入力の支援を受けられます。
例えば、Eclipseで
String.
と打つとStringクラスが持っているメソッドやフィールドが一覧表示されます。一覧から呼び出したいメソッドを選択すると、そのメソッドがコードに足されます。なので、クラスのメンバーを暗記しなくても済みますし、コードをタイプする手間が省けます。
このようなプログラマーを支援する機能がIDEにはたくさん備わっています。しかもIDEはどんどん進化していて、以前ならば自分の手で打ち込んでいたコードがどんどん自動入力されるようになっています。
また、IDEによって、実装されてる機能は異なるので、言語同様、慣れているものばかりずっと使うのではなく、より良いものはないか探して、良いと思ったものを積極的に使っていくと良いでしょう!
一つ一つはそれほど難しくないからやってきゃいい!
いろいろと紹介しましたが、どれも一つ一つはそれほど難しいものではなく、やっていけばマスターできるものばかりです。それらを一つ一つ取り組んでいったら、ある時、自分も上級者になったなぁと思うものなのだと思います。ってなわけで、紹介したものの中に知らないものがあった人はぜひ取り入れてみてください!
ad
関連記事
-
-
ITエンジニアになる方法と優秀なITエンジニアになる方法の違い?!
ITエンジニアになる方法 ITエンジニアになるにはコンピュータとプログラミングの基礎知識を学ぶ必要
-
-
若手社員時代に自己イメージを下げられるな!
私の新入社員時代の不遇 新入社員時代はなかなか自分の能力が会社から認めてもらえず苦悩していまし
-
-
就職や転職は、給料の高さや待遇よりも、やる意義のある仕事を求めよ
10年かかって辿り着いた結論を先に言うと 就職や転職は、給料の高さや待遇よりも、やる意義のある
-
-
効率的に新しい技術を学ぶ方法
効率的に新しい技術を学ぶための手順 IT技術は進歩が速く、次々と新しい技術が生み出されていきます。
-
-
やりたい仕事をやる方法
会社員がやりたい仕事を出来るかは運次第? 私は現在、フリーランスのITエンジニアをやっています。フ
-
-
古株メンバーがいくらバカでも賢く見えてしまうのは職場におけるストックホルム症候群が原因
ストックホルム症候群って知ってる? ストックホルム症候群はスウェーデンで起きた銀行強盗事件で人
ad
- PREV
- 効率的に新しい技術を学ぶ方法