Blog

ブログ

.NET コーディングのTips2選

初めに

ここ最近携わった案件でいくつか気になるコードを見かけましたので、多少なりとも改善できればとTips的なものを2つまとめてみました。
非常に初歩的な内容のため、今更言われなくても知ってるよ!という方はスルー推奨、初めて聞いた方は今後の参考にしていただければと思います。

foreachステートメント直前の要素数チェック

foreach の直前で要素数の判定を行っているコードを見かけますが、要素数が 0 の場合は何もせずブロックを抜けるため、要素の有無で処理分けが必要なければ判定は不要です。

例1:直前のif文で抜けるパターン

// このif文のブロックは不要
if (elements.Count == 0)
{
    return;
}
foreach (var element in elements)
{
    // 処理本体
}
return;

例2:if分のブロックとして処理するパターン

// このif文の判定は不要
if (elements.Count > 0)
{
    foreach (var element in elements)
    {
        // 処理本体
    }
}

いずれの場合も、 `foreach` のみ記述するだけで問題ありません。

foreach (var element in elements)
{
    // 処理本体
}

IEnumerableを実装するコレクションの要素存在チェック

IEnumerable を実装するコレクションに要素が存在するかどうかをチェックする際、 Count メソッドで取得した結果が 0 より大きいかで判定するコードを見かけますが、このメソッドは対象をカウントするために内部で全要素を列挙しているため、パフォーマンスがかなり悪くなります。
明確な要素数で判定する必要がない場合は Any メソッドを使うようにしましょう。

検証

0 から 999999999 までの数値を列挙したコレクションから 1000000 以上の要素が存在するかどうかを、Count と Any それぞれのメソッドで判定した場合の実行時間を比較します。

コード
using System.Diagnostics;

public class Example
{
    delegate bool CheckFunc(IEnumerable<int> list);

    /// <summary>
    /// 判定結果と処理時間を出力
    /// </summary>
    static void WriteResult(string name, CheckFunc func, IEnumerable<int> list)
    {
        Console.Write($"{name} : ");

        // 時間計測開始
        var sw = new Stopwatch();
        sw.Start();

        if (func(list))
        {
            Console.Write("要素が存在します");
        }
        else
        {
            Console.Write("要素が存在しません");
        }

        // 時間計測終了
        sw.Stop();

        Console.WriteLine($" : {sw.ElapsedMilliseconds} ミリ秒");
    }

    /// <summary>
    /// 0から999999999までの整数を列挙
    /// </summary>
    static IEnumerable<int> GetValues()
    {
        for (var i = 0; i < 1000000000; ++i)
        {
            yield return i;
        }
    }

    public static void Main()
    {
        // コレクションを取得
        var array = GetValues();

        // 拡張メソッドを利用して要素を抽出
        // このコードでは1000000以上の値を抽出
        var result = array.Where(x => x >= 1000000);
        // 次のようにクエリ式で書くことも可能(現場によってはNGかも)
        // var result = from x in array where x >= 1000000 select x;

        // 結果に要素が存在するかどうかを判定
        // Anyメソッドの場合
        WriteResult("Any", x => { return x.Any(); }, result);
        // Countメソッドの場合
        WriteResult("Count", x => { return x.Count() > 0; }, result);
    }
}
実行結果
Any : 要素が存在します : 23 ミリ秒
Count : 要素が存在します : 18756 ミリ秒

処理にかかる時間は環境によって変わりますが、同じ判定結果を得るのにかなりの差が出ることを確認できます。

また、抽出した要素を再利用しない場合はメソッドチェーンで簡潔に書くこともできます。

if (array.Where(x => x >= 1000000).Any()) {
    // 処理
}

簡潔にパフォーマンスを考慮したコードを書くように意識したいですね。

次回は

今回出てきた IEnumerable に関連して LINQ の解説やTipsをまとめてみたいと思います。

Shopifyで構築。「DMMLunaサイト」をリリース!

合同会社DMM.com様(以降、DMM様)のご依頼により女性向けに検査キットを販売するサイト、

DMMLunaサイトを2022年10月31日にリリースいたしました。

オリジナルデザインで、Shopifyで構築した案件になります。

https://dmmluna.com/

 

DMMLunaブランドを確立すべく世界観をどう表現するかを熟思

今回DMM様の新たなサービスの試みとして、本サイトを立ち上げることとなりました。

課題として承ったミッションは、DMMLuna独自のブランディングがいかに確立できるかです。

その実現のため、いくつかの試行錯誤を行いデザインに望みました。

扱う商材をどう見せるか!?議論

まず課題に上がったことは商材をどう見せるかについてです。

本サイトで扱う検査キットとは、以下のような商材になります。

この商材写真をいくら飾っても、目指すデザイン感にマッチさせることはできません(;_;)

そしてチームメンバーとディスカッションした末、検査キットの写真は用いず、ターゲットとなる女性写真を主役に、検査項目の数を明示して見せる!

という案で世界観を壊すことなくまとめ上げることができました。

この案に行き着くまで他のアイディアも出ており、例として

「イラスト化する案は?」「商品画像の更新性負荷や、商材の正確な再現性が劣るのでは?」といった理由により見送りになった経緯も背景にあったのです。

・FIXした商材画像が見れるページはこちら!

https://dmmluna.com/collections/kit

 

ロゴへ込めた思い&ストーリー性を持ったキャラクター設定

次にオリジナルの世界観の実現です。

ベンチマークとなるサイトは、そのサイト独自に描き起こしたイラストや撮り下ろしの写真が起用されていました。

本案件ではイラストレーターの起用が予算等の兼ね合いにより厳しく、条件として担当デザイナーができる範囲で制作しなければなりません。

またセンシティブな内容を扱った商材となるため、サイト用の写真撮影も見送る必要がありました。

その制約ある中で、ミッションとしてDMMLuna独自の世界観を表現する必要があったのです。

更にDMM様がベンチマークとなるサイトを大変気に入っていたため、それを凌駕するサイトを作らなければ!!という課題もありました。

そして提案させて頂いた内容は以下になります。

  • DMMLunaが描くストーリー展開

    (メインビジュアルにおいて)

    ・ルナ先生の元に本サイトのターゲットとなる女性をイメージしたイラストを配置。

    ・お家は、自宅で検査することができる。

    ・ポストは、ユーザーが検査した検査キットを郵送する。

    ・傘の子は、安心を届ける。

    といったメッセージを込め構成。

 

  • サイトロゴ

    シンボルの形状から「優しく包む」を表現。

    全体の形はハートを想起させ 「心」「優しさ」「安心」を。

    内側に包む形状はDMMLunaが女性の健康を守り、 優しく包み安心をもたらすという意味をかたどる

 

  • コンセプト

    DMMLunaをきっかけに新しい価値観や新たな生活習慣の場を本サイトを通じてユーザーに与えられるよう、DMMLunaブランドを確立できるようデザイン。

    今までターゲットとなるユーザーが利用していたサイトはなんとなく気持ちがのらない、面倒くさいといったネガティブなイメージを与えていたかもしれません。

    DMMLunaではそういったイメージを変え、DMMLuna独自の世界観を演出した本サイトで一新を目指す。

    ※このコンセプトは、これからの顧客満足度の向上、そして市場シェアの拡大にもつながることも目的といたしました。

 

ご提案した世界観はDMM様に評価いただき、仕上がったデザインを通じてご要望に沿った世界観を作ること結実できたのです。

 

「よくあるご質問」の見せ方を思案する

ここでは制作エピソードのこぼれ話を。

よくあるご質問は一般的に以下のような文字ベースの印象が多いかと思います。

今回DMM様から

「文字文字の印象ではなく、キャッチーで読みやすい見せ方にしたい!」とご要望を受けておりました。

実現に向け、イラストを入れる、URLの文字列をそのまま記載しない等

チームメンバーと工夫はしてみたものの、テキスト原稿のボリュームがある程度あると、実現することは困難なことが分かりました。

(本サイトは制約上、これ以上テキスト原稿の文字数を減らすことはできませんでした)

そして仕上がったページはこちら!

https://dmmluna.com/pages/faq

とはいえ、一般的な「よくあるご質問」ページより読みやすくキャッチーな印象に仕上がっていますよね★

Shopifyで構築することで運用性UPの恩恵を享受

まだまだShopifyを勉強中の私。

今回Shopify構築を通していくつか気づきがありました!

Shopify構築をクライアント様に提案する際に、メリットとしてお伝えできるかと思います。

管理画面でカンタンに更新ができる

今回Shopify側で用意されているテーマ*を元にオリジナルデザインの適用を行いました。

Shopifyではオリジナルのデザインを適用しても、管理画面からGUIで更新できる(ソース更新ではなく、ビジュアルベースで更新できる)仕組みが作れます。

 

Shopifyで構築すれば、運用者側でソース更新することなく、制作者側に依頼かけずとも

ブログのようにGUIで更新できる!のです。

つまりShopifyを使うと、手間なくノンテックの方でもとても簡単に運用する体制が築けます。

テーマとは:デザインのテンプレートのようなもの。テーマをそのまま使い構築もできますし、テーマを元にオリジナルデザインを作ることもできます。

戦略に合わせて、コンテンツの順序を並び替えられる

クライアント様のご要望として「更新頻度が高いコンテンツを上部に配置したい」という声が寄せられます。

例えば、「ブログコンテンツ」や「お知らせ」等々。

一方でストーリー展開を意識した場合、初めてユーザーに見てもらうサイトの場合、順序としてまずは自己紹介から入りますよね。

「私は◯◯です。◯◯◯をやっているサイトです。よろしく!」

このサイトはなんぞや的な紹介ですね。

この構成を優先とした場合、ページ最上部に配置することが適しています。しかし時が経ちサイトリリース後、リピーター層が多くなった場合は、ユーザーにとって何度も見なくてもよいコンテンツになるため、下に配置変更したくなる時が訪れるかもしれません。

この点Shopifyでは柔軟に応えることができます!

先程紹介した管理画面のGUIで上下に移動させ変更することが容易にできるのです。

つまり、サイトの戦略に合わせてコンテンツの配置をいつでも簡単に変更できるので叶えられるのです。

構築のポイントは、

配置を変更しても自然な見え方になるようデザインする!です。

今回紹介した以外でもShopifyが得意としていることはたくさんありますので、構築の検討をされている方はお問い合わせ頂き、ご質問いただけたらと思っております。

 

株式会社ジョーレンではオリジナルのデザインから、Shopifyのテーマ(テンプレート)を使った構築まで、業種ジャンル問わず幅広く承っております。

「こういった機能を付けたい」等、機能面に関するご相談も承ること可能ですので、お気軽にご相談ください。

EC業界トップクラスの実績!自社ECサイト構築が398,000円から
EC業界トップクラスの実績!自社ECサイト構築が398,000円から

 

【ECCUBE4系】MemcachedにのせたDoctrineCacheのCacheClearコマンドを自作してみる

今日はcacheのお話です。

※今日は、大好きなEventSubscriberの出番はありません※

前置き

複数台構成でECCUBEを運用するときに、気になるのはcacheですよね!

cacheの恩恵は大きいですが、サーバーが複数台構成の時は困ってしまうことも。。。

今回は、3台構成のWebサーバーでDoctrineCacheをMemcachedで管理するときに、DoctrineCacheのクリアコマンドを自作したときのお話です。

DoctrineCacheをどうやってMemcachedにのせるの。。。?という内容は、ご要望があれば別日に書こうかと思ってます。

なので、今回はDoctrineCacheをどうやってMemcachedにのせる設定とかは割愛させて頂きます。

 

実際に作成したコマンド

app/Customize/Command配下に以下のファイルを作成しました。

<?php

namespace Customize\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class DoctrineCacheClearCommand extends Command
{
    protected static $defaultName = 'memcached:doctrine-cache:clear';

    const SESSION_PREFIX = 'session';

    /**
     * @var SymfonyStyle
     */
    protected $io;

    protected function initialize(InputInterface $input, OutputInterface $output)
    {
        $this->io = new SymfonyStyle($input, $output);
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        try {
            $memcached = new \Memcached();
            $memcached->addServer(env('MEMCACHED_HOST'), env('MEMCACHED_PORT'));
            $keys = $memcached->getAllKeys();

            foreach ($keys as $key) {
                if (substr($key, 0, 7) !== self::SESSION_PREFIX) {
                    $memcached->delete($key);
                }
            }

            $this->io->success('Doctrine Memcached All Clear.');
        } catch (\Error $e) {
            $this->io->error('Doctrine Memcached Clear Failure Error.');
        } catch (\Exception $e) {
            $this->io->error('Doctrine Memcached Clear Failure Exception.');
        }

        return true;
    }
}

処理の説明

今回はsessionもMemcachedにのせているので、Memcachedをflush_allするというわけにはいきませんでした。

session情報も消えてしまうので、releaseする度にCustomerが再ログインしなければならないなんて、ユーザビリティ悪すぎですもんね。。。

 

方法はとてもシンプルです!

Memcachedから全てのkeyを取得して、sessionのプレフィックスがついているもの以外を順次削除していくだけです!

本当は。。。

DoctrineCacheにプレフィックスを作成して、そのプレフィックスのkeyを取得して消すという方法を取りたかったのですが、DoctrineCacheの時はプレフィックスをつけられなかったのです。。。

まだまだ勉強不足なんでしょうね。。。

 

使い道

管理画面からキャッシュクリアを行うときに合わせて呼び出すように改修したり。。。

デプロイ用のスクリプトに組み込んでみたり。。。

サーバーで直接叩いてみたり。。。

と、使っています。

サーバーが複数台構成でも、cacheとは上手く付き合っていきたいですよね、やはり画面の表示速度が違います!

 

最後に

今回は自作でキャッシュクリアのコマンドを作ってみた時のお話でした。

最近は画面側を作成するより、外部連携等のBatch作成の方が楽しいなーなんて思ってます。

この機会に、サーバーが複数台構成でもcacheと上手くお付き合いできる方法を考えてみませんか?