開発コーディングについて
1.はじめに
Java開発コーディングから自分自身の知識・経験のアウトプットと一般的な正しいコーディングの定義を確認するためレポートを作成しました。
この記事では、基本的な5つの観点について確認していきます。
この5点を守ることで必要最低限のコーディングの知識を身に着けるようになるはずです。
2.正しいコーディングとは
「開発コーディングは正しく行うべきである。」というのは非常に大切な考え方です。
しかし、何をもって正しいとするのかについて考える時間は少ないように思います。
なぜなら、実際の開発作業ではその開発で決めたルールに従い、進めることが多いからです。(ルールそのものが一般的には正しいものになってはいるとは思います。)
正しいコーディングを行うことで、「可読性」「保守性」「生産性」が向上するというメリットがあります。
正しいコーディングを行うために今回は以下の5つの項目について説明します。
2-1.変数のスコープを小さくする
変数のスコープとは、その変数が有効である範囲のことを指し、スコープを小さくすることで、変数が不必要に他の部分からアクセスされるのを防ぐことができます。
これにより、意図しないバグの発生を防ぎ、メモリ効率を向上させることができます。
また、変数スコープを小さくすることでコードの可読性が向上し、他の開発者がコードを理解しやすくなります。
下記に悪い例と良い例についてまとめました。
【悪い例】
public class ScopeExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
int sum = 0;//宣言が早すぎる
numbers[2] = 6;//配列の値を更新
for (int num : numbers) {
sum += num;
}
System.out.println("Sum: " + sum);
}
}
【良い例】
public class ScopeExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
numbers[2] = 6;//配列の値を更新
// 合計値はこのブロック内でしか使わない
{
int sum = 0;
for (int num : numbers) {
sum += num;
}
System.out.println("Sum: " + sum);
}
// sum はここでは使えない(スコープ外)
}
}
悪い例では”int sum”の宣言と実際に使用する箇所までが離れていて可読性が落ちてしまいます。(あくまで例は極端な話です。)
また仮にこのソースコードに修正が入ったときに、本来「numbers配列の要素の合計値」を出力するため処理だったものが、”int sum”に何か違う値を入れ込むようなことになってしまうかもしれません。
良い例では”int sum”は使用するすぐ近くで宣言され、またブロック内に入れることで不必要に他の部分からアクセスされるのを防ぐことができています。
2-2.単一ソースの原則を守る
ソフトウェア設計における重要な原則の一つで、クラスやモジュールは「一つの責任」のみを持つべきであるということです。
これにより、コードの可読性や保守性が向上し、変更が必要な場合にも影響範囲を最小限に抑えることができます。
この原則を遵守することで、データの一貫性を保ちメンテナンスの手間を減らすことができます。
下記に悪い例と良い例についてまとめました。
【悪い例】
// Userクラスがビジネスロジックとメール送信の責任を持っている
class UserService {
public void registerUser(User user) {
// データベースにユーザーを保存する責任
System.out.println("Saving user to DB: " + user.getName());
// メールを送信する責任
System.out.println("Sending email to: " + user.getEmail());
}
}
【良い例】
// ユーザー登録の責任のみ
class UserRegistrationService {
public void registerUser(User user) {
System.out.println("Saving user to DB: " + user.getName());
}
}
// メール送信の責任のみ
class EmailService {
public void sendEmail(User user) {
System.out.println("Sending email to: " + user.getEmail());
}
}
// 利用側
class Application {
public static void main(String[] args) {
User user = new User("Alice", "alice@example.com");
// 役割が明確に分かれている
UserRegistrationService registrationService = new UserRegistrationService();
EmailService emailService = new EmailService();
registrationService.registerUser(user);
emailService.sendEmail(user);
}
}
悪い例では”UserService”クラスの’registerUser’メソッドが呼びだされると、’user’の名前とメールアドレスの二つを参照するような形になっています。
良い例では名前を参照するメソッドを持つ””とメールアドレスを参照するメソッドを持つ””
2-3.適切な名前をつける
名前はコードの可読性と理解のしやすさに直結するため、適切な名前を選ぶことは非常に重要です。
変数名、メソッド名、クラス名など、すべての名前はその役割や機能を正確に表現するものであるべきです。
一貫した命名規則を採用することで、プロジェクト全体のコードの一貫性と品質を保つことができます。
下記に悪い例と良い例についてまとめました。
【悪い例】
public class Userinterface{
// クラス名は各単語の先頭を大文字にしていない
private String Userskill = "dive";
//変数名は最初の単語は大文字ではじめ、単語の区切りは大文字にしていない
private String final Usersname = "Mike";
//定数名は最初の単語は大文字ではじめ、単語の区切りは大文字にしていない
public void userauthenticated() {
//メソッド名は最初の英単語を小文字ではじめ、以降の単語の先頭を大文字にしていない
//メソッド名が動詞で始まっていない
}
}
【良い例】
public class UserManager {
// クラス名は各単語の先頭を大文字にする
private String userName = "John";
//変数名は最初の単語は小文字ではじめ、単語の区切りは大文字にする
private String final userNumber = "040425";
//定数名は最初の単語は小文字ではじめ、単語の区切りは大文字にする
public void calculateTotalAmount() {
//メソッド名は最初の英単語を小文字ではじめ、以降の単語の先頭を大文字にする
}
}
クラス名は、単語の先頭を大文字にし、複数の単語を組み合わせる場合も各単語の先頭を大文字にします。
メソッド名は、最初の単語は小文字で始め、以降の単語の先頭を大文字にします。処理内容を明確にするため、動詞で始めることが推奨されます。
変数名は、先頭は小文字で始め、単語の区切りは大文字にします。変数名はその役割を明確に表現する必要があります。
上記のような命名の仕方をこの書き方を「キャメルケース」と言います。
2-4.function(object)の形よりobject.function()の形で記載する
object.function()形式はオブジェクト指向プログラミング(OOP)の原則に従い、オブジェクトが自らのメソッドを呼び出す形を取る。 これによりコードはより直感的で理解しやすくなる。
下記に悪い例と良い例についてまとめました。
【悪い例】
public class User {
public static void printUpper(String s) {
System.out.println(s.toUpperCase());
}
}
// 呼び出し
User.printUpper("taro"); // function(object) 形式
【良い例】
public class User {
private final String name;
public User(String name) {
this.name = name;
}
public void printUpper() {
System.out.println(name.toUpperCase());
}
}
// 呼び出し
User user = new User("taro");
user.printUpper(); // object.function() 形式
悪い例にはfunction(object)の形、良い例にはobject.function()の形でのソースコードを記載しています。
悪い例ではメソッドの引数(’object’の部分に’hello’)をいれています。
例のメソッドだけ見れば特に問題はなさそうですが、いちいち引数を書いていると手間になります。
そこで良い例を見てみると、インスタンス生成時にメソッドで出力される部分(’hello’)を決めることでそのあとメソッドを呼ぶ出すだけで同じ結果を得ることができるようになっています。
2-5.継承より包含とインターフェースを好む
継承はオブジェクト指向プログラミングの基本概念の一つですが、過度に依存すると柔軟性が欠如しコードの再利用性が低下することがあります。
一方で、包含とインターフェースは、より柔軟で管理しやすいコードを実現するための強力なツールです。
包含とインターフェイスを優先することでコードの可読性、保守性、再利用性が向上します。
下記に継承とインターフェイスの例についてまとめました。
【継承の例】
abstract class Animal {
abstract void makeSound();
}
class Dog extends Animal {
String voice = "bow-wow"
@Override
void makeSound() {
System.out.println(voice);
}
}
【インターフェイスの例】
interface Calc {
int NUM1 = 1;
int NUM2 = 2;
void calc();
}
class Add implements Calc {
@Override
public void calc() {
System.out.println(NUM1 + NUM2);
}
}
まず、継承は親クラスの機能を子クラスに引き継ぐために使用されます。共通の性質や動作を持つクラス間でコードを共有したい場合に適しています。
状態(フィールド)を持つ必要がある場合や、protectedアクセス修飾子を利用したい場合に有効です。
ただし、継承は単一継承しかできないため、複数の性質を持たせる場合には制約があります。
次に、インターフェイスはクラスに振る舞いを付与するために使用されます。複数の振る舞いをクラスに付与したい場合に適しています。
状態を持たない純粋な振る舞いを定義するのに適しています。
3.自身の体験談について
先述した5つの項目について、私のこれまで経験談を以下にまとめました。
3-1.変数のスコープについて
これまでの開発作業の中で個人的に一番意識ができていなかった項目でした。
コンパイルエラーをなくすことばかりを考え、他の部分からのアクセスできるかもしれないという観点が欠如していました。
ありきたりな変数名(例えばname、age、sumなど)を使用していたためどこかで上書きされてしまう可能性もありました。
また、特に大規模な開発になると作業者が多く、同じ人が同じ場所を修正するわけではありません。
スコープが広いと対象変数の利用方法を確認するために、宣言個所や代入個所などをソース全体を見ることになり、手間もかなりかかってしまいます。
ですので、私を含め皆さん(特に開発経験が少ない方)はガイドや有識者の方に確認することを徹底した方が良いでしょう。
3-2. 単一ソースについて
クラスやモジュールの一つ一つの作りを一意に定めるということについては、開発作業でのルールが厳しく決まっていたこともあり、これまで十分に意識することができました。
例えば、画面遷移を行うイベント毎やデータベースのアクセスするテーブル毎に一つずつモジュールを用意し、開発を行っていました。
そのおかげでモジュールの修正を行う際の影響範囲の調査や実際のモジュールの修正など保守のしやすさを実感することができました。
3-3. 変数、メソッド、クラスの命名について
命名規則についてもこれまでの開発作業ではルールが決まっており、多くの作業者が作業するという前提の下で分かりやすいモジュール名・変数名を付けるように決まっていました。
変数名の命名についてイレギュラーではありますが以下のようなことがありました。
変数名は英単語を使用するのが一般的です。ただ、業界特有の日本語を英単語に訳すと非常に難しくなり、可読性が落ちてしまいます。なので、あえてローマ字で命名するということがありました。
以下例です。
日本語が「政治」とした時、英単語は「Politics」になるが、これを変数名として定義する際にローマ字で「seiji」とするような形である。(※あくまで例として挙げたものです。英単語「Politics」が一般的に難しいかどうかはここでは議論しません。)
3-4.object.function()について
単一ソースの原則とともにオブジェクト指向のプログラミングを遵守できていました。
クラス名(機能名)に応じたメソッドの定義や、他からのアクセスを考えたアクセス修飾子を使用してモジュールを作成することができました。
ルールが徹底されていたため、プロジェクト全体として生産性が上がっていたように思います。
3-5.インターフェースについて
私はこれまでの開発作業では、継承・包含などについてはあまり意識できていませんでした。
というのも先述した現場のルールに則り開発作業を行っていたので、「包含とインターフェースは、より柔軟で管理しやすいコードを実現する」という観点がそこまで身につきませんでした。
今回改めてコーディングについて確認したことで、クラスごとの特性や振る舞いを理解し、実装に落とし込むことの大切さを知ることができました。
今後の開発では継承関連の部分はより一層意識して行おうと思います。
4.最後に
本記事では、正しいコーディングの定義について確認しました。
結果としては、私自身は一般的に正しいコーディングについて認識を改めることができました。
「2-1.変数のスコープを小さくする」、「2-5.継承より包含とインターフェースを好む」の二つについては今回レポートを書き、改めて内容を確認した時に、自分自身の観点として足りなかったところだと知ることができました。
今後の開発でも、自分のコーディングは正しいコーディングとなっているかを定期的に振り返りながら進めていこうと思います。
また皆様も正しいコーディングとは何かを考える・確認してみることによって新たな気づきがあるかもしれません。
本記事がそのきっかけになっていると幸いです。
