nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

Modern_C++_leet文字列変換

#include <string>
#include <iostream>
#include <map>
#include <algorithm> // transform
#include <vector>
#include <numeric> //iota, accumulate

// leetマップ変数 (グローバル変数)
std::map<char, char> leet {
    {'A', '4'},
    {'E', '3'},
    {'G', '6'},
    {'I', '1'},
    {'O', '0'},
    {'S', '5'},
    {'Z', '2'}
};
        
/**
 * @fn
 *  入力文字列をLeet文字列に変換する関数
 * @brief 要約説明
 * @param input 入力文字列
 * @return  Leet変換後の文字列
 * @detail 変換規則はグローバル変数に記載したleetマップ変数に記載
 */
std::string ConvertLeetString(std::string input)
{
    
    std::vector<char> result;
    
    // Map(変換)
    // 入力文字列を1文字ずつLeet文字列に変換する。変換結果はcharを格納するvectorに格納する
    std::transform(input.begin(), input.end(), std::back_inserter(result),
    [=](char item) -> char 
    {
        std::map<char, char>::iterator it;
        
        it = leet.find(item);

        if (it != leet.end())
        {
            // 探索文字(item)と等価なキーがleetマップ変数にある場合はそのvalue値を返す
            return leet[item];
        }
        return item;
    });
    
    // Accumulate(集約)
    return  std::accumulate(result.begin(), result.end(), std::string());
}

int main(void)
{
    // インプット文字列を取得
    std::string input;
    std::cin >> input;
    
    // ConvetLeetStringメソッドを保有するUtilityクラスにしたほうがよかった
    std::string result = ConvertLeetString(input);
    
    std::cout << result << std::endl;

    return EXIT_SUCCESS;
}

UML学習まとめ

UML学習全般を記載する記事です

関連端名(ロール名)

関連の両端にそれぞれのクラスのロール名を記述することができます。

ロール名は相手方クラスとの関連における自分のクラスの役割(role)を示します。

以下の記事を参照ください。

以下の記事を参照ください。

関連名

関連線の中央に関連の名前を記述することができます。一般的には動詞です。

社員クラスと会社クラスがあり、間を関連線でつなぎます。関連名は主語にするクラスで変わります。

  • 主語を社員クラス側にすると、関連名は「勤務する」
  • 主語を会社クラス側にすると、関連名は「雇用する」

以下の記事を参照ください。

UMLはベンダー資格試験があります

理解度確認のために試験を受けてみるもいいかもしれません。

UMLツール使い方

モデル駆動開発でUMLは使用されます

モデル駆動開発(MDD:Model-Driven Development)」(注:MDDはOMGの商標になっています)は、分析・設計・実装・テストといった開発の成果をモデルとして作成し、モデル変換を繰り返し適用することによってプロセスを進める開発手法

Observerパターン [C#] [pull型]

Observerパターンとは

あるオブジェクトの状態に依存するオブジェクトがあるとき、状態の変化をそれらのオブジェクトに依存せずに自動的に通知するパターン

push型とpull型

Observerパターンでは,push型とpull型があります。違いは以下のとおり。

  • push型 : updateの引数に変更された状態を渡すことで、Observerに通知する
  • pull型 : updateを呼び出されたタイミングでObserverがObservableに状態を問い合わせる

本記事では、pull型のObserverパターンについて説明します。

参考

Observerパターンのpull型のクラス図

f:id:nprogram:20190801081705p:plain

コード

using System;
using System.Collections.Generic;

namespace Observer_Pattern_push_type
{
    class Program
    {
        static void Main(string[] args)
        {
            Person model = new Person();
            View view = new View();
            view.DataSource = model;    // データバインド

            model.Id = 123;
            model.Name = "Ichiro";
        }
    }

    interface IObserver
    {
        void Update(Observable observer);
    }

    interface ISubject
    {
        void Add(IObserver item);
        void Delete(IObserver item);
        void Notify();
    }

    /// <summary>
    /// テキスト表示用のUI部品 (ダミー)
    /// </summary>
    class TextControl
    {
        public string Text
        {
            set { Console.WriteLine("TextControl is updated: {0}", value); }
        }
    }

    /// <summary>
    /// 監視するクラス
    /// </summary>
    class View : IObserver
    {
        TextControl nameText = new TextControl();
        TextControl idText = new TextControl();

        public void Update(Observable item)
        {
            var person = item as Person;

            if (person != null)
            {
                nameText.Text = person.Id.ToString();
                idText.Text = person.Name;
            }
        }

        public Person DataSource
        {
            set { value.Add(this); }
        }
    }

    /// <summary>
    /// 監視されるクラス
    /// </summary>
    abstract class Observable : ISubject
    {
        private List<IObserver> _observers = new List<IObserver>();

        public void Add(IObserver item)
        {
            _observers.Add(item);
        }

        public void Delete(IObserver item)
        {
            _observers.Remove(item);
        }

        public void Notify()
        {
            _observers.ForEach(observer => observer.Update(this));
        }

        protected void Update() // 更新イベント
        {
            Notify();
        }
    }

    /// <summary>
    /// Modelクラス
    /// </summary>
    class Person : Observable
    {
        private int _Id;
        public int Id
        {
            get { return _Id; }
            set
            {
                if (value != _Id)
                {
                    _Id = value;
                    Update();
                }
            }
        }

        private string _Name;
        public string Name
        {
            get { return _Name; }
            set
            {
                if (value != _Name)
                {
                    _Name = value;
                    Update();
                }
            }
        }
    }
}
TextControl is updated: 123
TextControl is updated:
TextControl is updated: 123
TextControl is updated: Ichiro

Observerパターン [C#] [push型]

Observerパターンとは

あるオブジェクトの状態に依存するオブジェクトがあるとき、状態の変化をそれらのオブジェクトに依存せずに自動的に通知するパターン

push型とpull型

Observerパターンでは,push型とpull型があります。違いは以下のとおり。

  • push型 : updateの引数に変更された状態を渡すことで、Observerに通知する
  • pull型 : updateを呼び出されたタイミングでObserverがObservableに状態を問い合わせる

本記事では、push型のObserverパターンについて説明します。

Observerパターンのpush型のクラス図

f:id:nprogram:20190731224438p:plain

各クラスの役割

  • Subject
    • Observer の登録と削除、状態変化の通知の機能を定義する
  • Observer
    • Subject から通知を受け取る機能を定義する
    • ConcreteSubject‐ConcreteObserver 間の結合を抽象的に(直接的な依存関係を排除)するためのインターフェース
  • ConcreteSubject
    • Observer に状態の変化を通知する。
    • Observer とは一対多の関係。
    • 状態の変化を登録されているすべてのObserverに通知する責務を持つ
    • 複数の異なる ConcreteObserverに対応できるように、抽象的に Observer のリストを保持する
    • 被観察者
    • 別名として観察可能な(Observable)クラスとも呼ばれる
    • ConcreteSubjectはConcreteObserverの存在を知らない。Observerの存在は知っている
  • ConcreteObserver
    • Subject の状態が変化されたことが通知される
    • 状態変化による無矛盾性を保つ
    • 観察者

コード

using System;
using System.Collections.Generic;

namespace Observer_Pattern
{
    class Program
    {
        static void Main(string[] args)
        {
            ConcreteObserver observer1 = new ConcreteObserver();
            ConcreteObserver observer2 = new ConcreteObserver();

            ConcreteSubject subject = new ConcreteSubject();

            subject.Add(observer1);
            subject.Add(observer2);

            Random rnd = new System.Random();

            for (int i = 0; i < 3; i++)
            {
                // Set Random Value from 0 to 9
                subject.Status = rnd.Next(10);
            }
        }
    }

    interface IObserver
    {
        void Update(int num);
    }

    interface ISubject
    {
        void Add(IObserver item);
        void Delete(IObserver item);
        void Notify();
    }

    class ConcreteObserver : IObserver
    {
        public void Update(int num)
        {
            Console.WriteLine("Update Value: {0}", num);
        }
    }

    class ConcreteSubject : ISubject
    {
        private List<IObserver> _observers = new List<IObserver>();

        public void Add(IObserver item)
        {
            _observers.Add(item);
        }

        public void Delete(IObserver item)
        {
            _observers.Remove(item);
        }

        public void Notify()
        {
            _observers.ForEach(observer => observer.Update(_Status));

            //foreach (var item in _observers)
            //{
            //    item.Update(_Status);
            //}
        }

        private int _Status;
        public int Status
        {
            get { return _Status; }
            set
            {
                _Status = value;
                Notify();
            }
        }
    }
}
Update Value: 4
Update Value: 4
Update Value: 2
Update Value: 2
Update Value: 5
Update Value: 5

参考リンク

デバッグ技術

はじめに

ソフトウェア評価時では、デバッグは必要不可欠です。

以下のような手順で行うと思います。

手順

  1. バグを記録する (バグレポートを書く)
  2. バグを再現する
  3. バグを分離する (バグ発生原因を特定するために必要)
  4. バグを修正する

バグを記録する

バグ内容・バグ発生手順を記録します。

バグ発生時の環境(PC環境・試験対象ソフトウェアバージョン・試験対象ソフトウェア状態・他の接続機器)も正確に記載してください。

記録がないと、後で再現させることができません。

バグを再現する

バグを再現させます。簡単に再現できない場合もあります。

ソフトウェアの特定の動作タイミングや特定の通信のみで発生するバグなど簡単に再現できない場合は工夫が必要です。

バグを分離する

バグ発生原因を特定するために必要な作業です。

バグを修正する

その他の機能に影響を与えないように慎重にコードを修正します。

Windows標準のイベントビューアーを使用する

Windows のイベントビューアーを使って、何がいつ起きたか調べる

eventvwr.exe

コマンド実行フィールドにeventvwrと記載しても起動できます。

Windows のイベントビューアーを使って、何がいつ起きたか調べる | ラボラジアン

バグを再現させて、IDEのブレークポイントを使用する

デバッグの技術 | POSTD

Nullチェックを削除する方法 (NullObjectパターンとNull合体演算子)

はじめに

Nullチェックを削除する方法はいくつかあります。最適な方法を使用してください。

  • NullObjectパターン
    • オブジェクト指向プログラミングが使えれば、どの言語でも実現可能
  • Null合体演算子
    • 言語仕様にNull合体演算子があれば可能
  • その他の方法
    • Javaの場合はOptionalを使用

NullObjectパターン

NullObjectパターンを使用することで、Nullチェックのif文を削除できます。

クラス図

f:id:nprogram:20190619230456p:plain

コード例

次にように作成します。

  1. 何もない(本例では職種がない)ことを表すサブクラスであるNullEmployeeクラスを作成します。

  2. サブクラスのメソッド(本例ではgetTypeNameメソッド)は呼ばれても何も処理しないようにします

  3. CreatorクラスのfactoryMethodにおいて、不正なパラメータを受け取った場合は、NullEmployeeクラスを返します。

  4. 呼び出し元関数のおいて、Nullチェックのif文を削除します

  5. NullEmployeeクラスのインスタンスが存在するため、NullPointerExceptionの例外が発生しません。

本記事に記載されていない他のクラス(Salesman, Engineer, Manager, Employee)のコードは、以下の記事を参照ください。

switch文を爆散 - nprogram’s blog

[NullEmployee .java]

// 具象クラス
public class NullEmployee extends Employee
{
    public NullEmployee(int typeId)
    {
        _typeId = typeId;
    }
    
    @Override
    public int getTypeId()
    {
        return _typeId;
    }
    
    @Override
    public String getTypeName()
    {
        // Nullオブジェクトであるため、処理が空っぽの状態とする
        return "";
    }
}

[CreatorEmployee.java]

// 本クラスは継承を禁止するため、final修飾子を付与します
public final class CreatorEmployee
{
    // 本例では、定数の定義はCreatorEmployeeクラスが持ちます
    // もし、列挙型(Enum)を使いたいなら、以下のサイトを参考にしてください
    // https://qiita.com/KeithYokoma/items/9681b130ea132cfad64d
    
    public static final int ENGINEER = 0;
    public static final int SALESMAN = 1;
    public static final int MANAGER = 2;
    public static final int ILLEGAL_EMPLOYEE = -1;
    
    // コンストラクタをprivateにすることで、インスタンス化を禁止させましょう
    private CreatorEmployee(){ }
    
    // factoryMethodメソッドの役割は与えられたパラメータによって生成するインスタンスを切り替えること
    public static Employee factoryMethod(int type)
    {
        switch(type)
        {
            case ENGINEER:
                return new Engineer(ENGINEER);
                
            case SALESMAN:
                return new Salesman(SALESMAN);
                
            case MANAGER:
                return new Manager(MANAGER);
                
            default:
                System.out.println("Error!! Illegal Parameter.");
                return new NullEmployee(ILLEGAL_EMPLOYEE);
        }
    }
}

[Main.java]

import java.util.*;

public class Main
{
    public static void main(String[] args) throws Exception
    {
        // (1) インスタンスを生成
        // (2) 関数に作成したインスタンスを渡す
        
        Employee engineer = createEmployee(CreatorEmployee.ENGINEER);
        showTypeName(engineer);
        showId(engineer);
        
        Employee salesman = createEmployee(CreatorEmployee.SALESMAN);
        showTypeName(salesman);
        showId(salesman);
        
        Employee manager = createEmployee(CreatorEmployee.MANAGER);
        showTypeName(manager);
        showId(manager);
        
        Employee nullEmployee = createEmployee(-9999); // 不正値を入力する
        showTypeName(nullEmployee);
        showId(nullEmployee);
    }
    
    public static Employee createEmployee(int type)
    {
        return CreatorEmployee.factoryMethod(type);
    }
    
    
    public static void showId(Employee employ)
    {
        // Nullオブジェクト適用により、null比較のif文がなくなりました。
        System.out.println(employ.getTypeName());
    }
    
    public static void showTypeName(Employee employ)
    {
        // Nullオブジェクト適用により、null比較のif文がなくなりました。
        System.out.println(employ.getTypeId());  
    }
}
0
Engineer
1
Salesman
2
Manager
Error!! Illegal Parameter.
-1

null合体演算子

言語使用に、NullObjectパターンを使用するのではなく、言語使用として、NullObjectパターンを使用しなくともNullPointerException例外を回避できる仕組みがあります。

C#では「null合体演算子」(null coalescing operator、coalesceは「合体する」という意味の動詞)を使うと、nullチェックのif文を削除できます。

以下に、コード例を記載します。

[適用前]

public class Test{
    public static void Main(){

        string documentTitle = null;
        string initialTitle = "報告書";
    
        if (documentTitle == null) {
            documentTitle = initialTitle;
        }
        System.Console.WriteLine(documentTitle);
    }
}
報告書

Null合体演算子(C#では??)を使うと以下のように記載できます。これにより、Nullチェックのif文を削除できました

[適用後]

public class Test{
    public static void Main(){

        string documentTitle = null;
        string initialTitle = "報告書";
        
        string fileName = documentTitle ?? initialTitle;
        
        System.Console.WriteLine(fileName);
    }
}
報告書

その他の方法

Java言語の場合はOptionalを使うことで、NullPointerException例外を回避できそうです。

あとがき

NullObjectパターンを使用しなくともNullPointerException例外を回避できる言語仕様があれば、積極的に使うべきだと思います。

NullObjectパターンは、あまり使われなくなりそうです。