「技術」カテゴリーアーカイブ

デザインパターン】Flyweightパターン

Flyweightパターンのサンプルコードです。

package org.example.flyweight;

public class Flyweight {
    public void method1() {

    }

    public void method2() {

    }
}
package org.example.flyweight;

import java.util.HashMap;
import java.util.Map;

public class FlyweightFactory {
    private Map<Integer, Flyweight> pool = new HashMap<>();
    private static FlyweightFactory factory = new FlyweightFactory();

    private FlyweightFactory() {

    }

    public static FlyweightFactory getFactory() {
        return factory;
    }

    public Flyweight getFlyweight(int key) {
        Flyweight result;
        boolean exist = pool.containsKey(key);
        if(exist == true) {
            result = pool.get(key);
        } else {
            result = new Flyweight();
            pool.put(key, result);
        }

        return result;
    }
}
package org.example.flyweight;

public class Main {
    public static void main(String[] args) {
        FlyweightFactory factory = FlyweightFactory.getFactory();
        Flyweight object = factory.getFlyweight(0);
        object.method1();
        object.method2();
    }
}

Flyweightパターンは一度作成したインスタンスを保持して再利用することによってインスタンス作成時の負荷を軽減させるパターンです。

サンプルコードではFlyweightのオブジェクトをFlyweightFactory内のpoolに保持し、一度使用したものはpoolから取得するようにしています。

FlyweightFactoryはSingletonパターンで使用するのが一般的です。

poolは、サンプルではHashMapを使用しましたが、Collectionのインターフェースを持つクラス(Listなど)でも使用できます。

このあたりは実際の設計に合わせて実装していくことになると思います。

【ALEXAスキル開発】テレビスキルを作成開始

前回で大体の仕組みがつかめてきました。

この画面に設定した内容は、スキルの呼び出し名を設定します。

上の設定では、「テレビを開いて」と言えばスキルが起動します。

次にインテント。

「テレビ」スキルに対する命令のようなもの、ですね。

ここでは、VolumeUpIntentを作成して、これに対応するセリフを設定します。

上の場合は「ボリュームを上げて」「ボリュームアップ」と言えば、このインテントが作動します。

次にコード。

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    handle(handlerInput) {
        const speakOutput = 'テレビをどうしますか?';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .reprompt(speakOutput)
            .getResponse();
    }
};

const VolumeUpIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'VolumeUpIntent';
    },
    handle(handlerInput) {
        const speakOutput = 'ボリュームを上げました';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
            .getResponse();
    }
};

exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        VolumeUpIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        FallbackIntentHandler,
        SessionEndedRequestHandler,
        IntentReflectorHandler)
    .addErrorHandlers(
        ErrorHandler)
    .withCustomUserAgent('sample/hello-world/v1.2')
    .lambda();

まず上のLaunchRequestHandlerはスキル起動時の動作。

ここではスキル起動後、「テレビをどうしますか?」と応答がでます。

そして、VolumeUpIntentHandler。インテントがVolumeUpIntentならば「ボリュームを上げました」を返ります。

そして、最後のexports.handlerに追加したVolumeUpIntentHandlerを追加します。

動作はこんな感じになります。

「テレビ」を起動して「ボリュームを上げて」で設定した内容が返ってきました。

しかし、「テレビのボリュームを上げて」ではダメで、「テレビを開いてボリュームを上げて」と言わなければならないようです。

でも、これで一文でコントロールできそうです。

あとは、どうやってWebAPIを叩くか、ですね。

次回までに調べておきます。

【ダイエット支援】スマホから写真をアップロードできるようにする

さて、スマホのレイアウト崩れも直ったので、

これからやりたいことは、

・カメラで栄養成分表示を撮影してアップロードする

・それをOCRでテキストデータに変換し、数値を入力する

です。

まずは、スマホの画面にだけ、ファイルアップロードのコントロールを表示させます。

div#mobile {
    @include small {
    }
    @include middle {
        display: none;
    }
    @include big {
        display: none;
    }
}
                <div id="mobile">
                    <p>
                        <input type="file" name="file" />
                    </p>
                    <p>
                        <button>送信</button>
                    </p>
                </div>

メディアクエリの使い方覚えたから楽勝。

PCからはこうなってますが、

スマホからはこうなります。

で、ファイルを選択をタップすると、

こんな感じでカメラを起動して撮影したものをすぐにアップロードできるんですね。

とりあえず、今日やりたいことはできた。

次回は実際にOCRを組み込んでみたいと思います。

【COCOS2D-X】クエストリストの作成に着手

こんな感じでウィンドウを作成しました。

「冒険」ボタンをタップすると表示するようになっています。

これを縦に並べてクエストリストを表示させたいのですが、

データ構造はこんな感じで実装しました。

typedef struct _TextLabel {
    cocos2d::Label* label;
    cocos2d::Vec2 point;
} TextLabel;

typedef struct _QuestListMenu {
    Parts parts;
    TextLabel questName;
} QuestListMenu;

typedef struct _QuestList {
    Parts parts;
    QuestListMenu questListMenu[QUEST_NUM];
    float scaleRate;
} QuestList;

シーンの中ではnew演算子禁止なので、listは使えません。

散々悩んだのですが、とりあえず、大きめの配列を用意して使用することにします。

    if(isTouch(touch->getLocation(), &(questButton.parts)))
    {
        log("touch questButton");
        auto questList = getQuestList();
        auto questName = questList->begin();
        questListMenu.questListMenu->parts.sprite = Sprite::create("btn02_03_s_bl.png");
        questListMenu.questListMenu->parts.sprite->setAnchorPoint(Vec2(0.0, 1.0));
        questListMenu.questListMenu->parts.point = questListMenu.parts.point;
        questListMenu.questListMenu->parts.sprite->setPosition(questListMenu.questListMenu->parts.point);
        questListMenu.questListMenu->parts.size = Size(questListMenu.questListMenu->parts.sprite->getContentSize().width * questListMenu.scaleRate,
                                                       questListMenu.questListMenu->parts.sprite->getContentSize().height);
        questListMenu.questListMenu->parts.sprite->setScale(questListMenu.scaleRate, 1);
        this->addChild(questListMenu.questListMenu->parts.sprite, 5);
    }

タップ処理です。

今の段階では上の画面のようなウィンドウを作成追加するだけです。

今後は、数を増やしたり、テキストを追加したりする予定です。

デザインパターン】Facadeパターン

Facadeパターンのサンプルコードです。

package org.example.facade;

public class Class1 {
    public void method1()
    {

    }
}
package org.example.facade;

public class Class2 {
    public void method2()
    {

    }
}
package org.example.facade;

public class Facade {
    Class1 class1;
    Class2 class2;

    public Facade()
    {
        class1 = new Class1();
        class2 = new Class2();
    }
    public void function()
    {
        class1.method1();
        class2.method2();
    }
}
package org.example.facade;

public class Main {
    public static void main(String[] args)
    {
        Facade facade = new Facade();
        facade.function();
    }
}

Facadeパターンは、様々な機能を持つクラスの処理を、Facadeクラスの窓口に一本化するパターンです。

よく利用する一連の処理をFacadeのメソッドとして定義することで、扱いが簡単になります。

もしかしたら、デザインパターンを意識しなくてもやっているかもしれない。

【Alexaスキル開発】チュートリアルを始める

さて、最終目標はAlexaとの連携です。

なので、Alexaスキルの開発に着手していこうと思います。

しかしながら、スキル開発は全くの初めてなので、チュートリアルから始めていこうと思います。

こちらの記事に従って進めていきました。

https://developer.amazon.com/ja/blogs/alexa/post/31c9fd71-f34f-49fc-901f-d74f4f20e28d/alexatraining-firstskill

まぁ、初めはこんなもんでしょ。

次回以降もチュートリアルを進めていきます。

【ダイエット支援】スマホ画面でのダイアログ表示崩れを修正

前回の修正で

メディアクエリを使用すればレスポンシブデザインのコーディングができるよ、ということがわかりました。

では、これをsassにも適用させたいのですが、

sassに記入するには、ミックスインというものを使用するのが一般的なようです。

具体的には、こんな感じ。

@mixin small {
    @media (max-width: (479px)) {
        @content;
    }
}

@mixin middle {
    @media (min-width: (480px) and (max-width:959px)) {
        @content;
    }
}

@mixin big {
    @media (min-width: (960px)) {
        @content;
    }
}

@mixin XXでメディアクエリの定義に名前をつけ、この定義をsassの先頭あたりに記載します。

今回は大中小と記載しましたが、いいように名前を指定できます。

で、これをsassのスタイル定義に使用します。

    #content {
        z-index: 2;
        @include small {
            width: 90%;
        }
        @include middle {
            width: 50%;
        }
        @include big {
            width: 30%;
        }

こうすることでsassにもメディアクエリが適用できます。

いい感じになりました。

【COCOS2D-X】メニューボタンも作り直す

まずは、キャラクター詳細ウィンドウに表示パラメータを追加しました。

さらに、右側のメニューボタンをキャラウィンドウと同じように構造体を使って配置します。

今後、タッチ判定処理を追加することになるので。

class HomeScene : public cocos2d::Scene
{
private:
    CharaWindow window[4];
    CharaDetailWindow detailWindow;
    MenuButton homeButton;
    MenuButton charaButton;
    MenuButton equipButton;
    MenuButton questButton;
    homeButton.sprite = Sprite::create("btnHome.png");
    if(homeButton.sprite != nullptr)
    {
        homeButton.sprite->setAnchorPoint(Vec2(1.0,1.0));
        homeButton.point = Vec2(sprite->getPosition().x + sprite->getContentSize().width * scaleRate / 2 + origin.x, visibleSize.height + origin.y);
        homeButton.sprite->setPosition(homeButton.point);
        homeButton.size = Size(homeButton.sprite->getContentSize().width * buttonScale, homeButton.sprite->getContentSize().height * buttonScale);
        homeButton.sprite->setScale(buttonScale);
        buttonBase = visibleSize.height - homeButton.size.height;
        this->addChild(homeButton.sprite, 1);
    }

    charaButton.sprite = Sprite::create("btnChara.png");
    if (charaButton.sprite != nullptr)
    {
        charaButton.sprite->setAnchorPoint(Vec2(1.0,1.0));
        charaButton.point = Vec2(sprite->getPosition().x + sprite->getContentSize().width * scaleRate / 2 + origin.x, buttonBase + origin.y);
        charaButton.sprite->setPosition(charaButton.point);
        charaButton.size = Size(charaButton.sprite->getContentSize().width * buttonScale, charaButton.sprite->getContentSize().height * buttonScale);
        charaButton.sprite->setScale(buttonScale);
        buttonBase -= charaButton.size.height;
        this->addChild(charaButton.sprite, 1);
    }

    equipButton.sprite = Sprite::create("btnEquip.png");
    if (equipButton.sprite != nullptr)
    {
        equipButton.sprite->setAnchorPoint(Vec2(1.0,1.0));
        equipButton.point = Vec2(sprite->getPosition().x + sprite->getContentSize().width * scaleRate / 2 + origin.x, buttonBase + origin.y);
        equipButton.sprite->setPosition(equipButton.point);
        equipButton.size = Size(equipButton.sprite->getContentSize().width * buttonScale, equipButton.sprite->getContentSize().height * buttonScale);
        equipButton.sprite->setScale(buttonScale);
        buttonBase -= equipButton.size.height;
        this->addChild(equipButton.sprite, 1);
    }

    questButton.sprite = Sprite::create("btnQuest.png");
    if (questButton.sprite != nullptr)
    {
        questButton.sprite->setAnchorPoint(Vec2(1.0,1.0));
        auto questButtonScale = buttonBase / questButton.sprite->getContentSize().height;
        questButton.point = Vec2(sprite->getPosition().x + sprite->getContentSize().width * scaleRate / 2 + origin.x,
                                 buttonBase + origin.y);
        questButton.sprite->setPosition(questButton.point);
        questButton.size = Size(questButton.sprite->getContentSize().width * questButtonScale, questButton.sprite->getContentSize().height * questButtonScale);
        questButton.sprite->setScale(questButtonScale);
        this->addChild(questButton.sprite, 1);
    }

さて、次はタッチ判定でシーン切り替えと、クエストシーンの作成を行って行きます。

デザインパターン】Decoratorパターン

Decoratorパターンのサンプルコードです。

package org.example.decorator;

public abstract class Component {
    public abstract void method1();
    public abstract void method2();
}
package org.example.decorator;

public class ConcreteComponent extends Component {
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}
package org.example.decorator;

public abstract class Decorator extends Component {
    protected Component component;

    protected Decorator(Component component) {
        super();
        this.component = component;
    }
}
package org.example.decorator;

public class ConcreteDecorator extends Decorator {
    protected ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}
package org.example.decorator;

public class Main {
    public static void main(String[] args)
    {
        Component component = new ConcreteComponent();
        Component instance = new ConcreteDecorator(component);
        instance.method1();
        instance.method2();

        Component component1 = new ConcreteDecorator(new ConcreteDecorator(component));
        component1.method1();
        component1.method2();
    }
}

このパターンでは、中身(ConcreteComponent)と装飾(ConcreteDecorator)が存在し、Componentという共通のインターフェースを持っています。

機能を追加する場合はDecoreterを継承したクラスを作成し、機能を拡張することができます。

ConcreteDecoratorのコンストラクタにはConcreteComponentを渡すこともできますし、すでに作成したConcreteDecoratorを渡すこともでき、それによって再帰的な処理をすることもできます。

しかし、クラスの関係が複雑になったりするので、クラス図による整理が必要になりますね。