密码程序建模 - 继承和其他OOP概念

时间:2014-03-20 16:31:18

标签: c++ oop inheritance encryption

好的,所以我尝试过实现简单的单字母替换密码,如Caesars,digraph like playfair,polyalphabetic,如autokey,vigenre和其他一些c ++ {不使用类}。现在我想将所有这些密码和其他几个密码汇集在一起​​并将其打包成一个项目。我已经开始编写几行代码,但我不确定我必须如何设计它。这是我的课程的样子。

我的前端     //main.cpp包含很少的交换机案例来选择正确的加密密码。

//cipher.cpp implements class cipher.In a crude format the class looks like
class cipher
{
protected:
string plaintxt,ciphertxt;
public:
virtual bool encrypt()=0;
virtual bool decrypt()=0;
virtual bool tabulate()=0;
}

此类由cipher.h接口

//mono_alphabetic.cpp implemants the class mono_alpha

class mono_alpha : public cipher
{
 protected:
 map<string,string> Etable,Dtable;
 public:
 bool Encrypt();
bool Decrypt();
}

现在我在这里使用一个简单的atbash密码示例。对于那些不知道atbash密码是什么的人来说,它是一种加密模式,其中给定字符串中的每个字符都是用反向字母顺序的每个位置加上等效字符。例如。 A - &gt; Z Z-> A B-> Y M-> N等。

class atbash : public mono_alpha
{
  public:
  bool tabulate();  // makes a hash map were A is mapped to Z M to N e.t.c
atbash(string&);  // accepts a string and copies it to string plaintxt.

} 

这是一个非常粗略的例子。这里只介绍课堂设计。这里有一些疑点。

实现:我接受来自用户的字符串,然后将其传递给类atbash的构造函数,然后将其复制到从基类密码继承的字符串数据成员plaintxt。然后我会从构造函数调用函数制表。现在我有两个选择:tabulate()生成加密表的哈希映射并将其存储在Etable中,或者它也可以生成解密表。在atbash密码的情况下这些是一个但是一样的。但是一般的单字母替换密码的情况呢?我将如何强制制表功能来创建任何一个。

我的想法是将构造函数的字符参数传递给构造函数,该构造函数描述给定字符串是否要加密或解密,并相应地将其保存在plaintxt或ciphertxt中的任何一个中。此外,构造函数将此字符参数传递给制表表相应地将加密或解密表制成表格的功能。这样好吗?

关于如何改进这个的任何建议?

接口:我从main.cpp实现所有这些密码的接口的方法就是使用swith case。

switch(chosen_value)
{
case 1: cout<<"atbash encryption";
cipher*ptr = new atbash ("a string");

// ptr->tabulate();   if it isn't being called directly from the constructor.(here it is)

case 2:
cout<< "caeser's cipher";

.....................
.

.....
}     

有没有更好的方法可以在不使用switch case的情况下实现它。

同样你可以看到我已经使用了一个指向派生类对象的基类指针来执行此操作。我知道这里没有必要,我可以简单地继续声明一个对象。通过基类指针引用对象有什么重要意义吗? 我听说这些基类指针有时候可以成为真正的救世主!如果是这样,请指导我简化编码的场景。在这个特定的情况下,在基类中声明纯虚函数并没有任何用处。这只是膨胀代码吗?

我应该继续将类实现分离成单独的文件,就像我在这里完成的那样,或者我应该在一个main.cpp中将所有这些代码捏起来,这样可以使继承变得更容易,因为你没有使用头文件。

请指导我。我没有专业的编码经验,我很乐意在这里提出您的意见。

1 个答案:

答案 0 :(得分:3)

一些想法,没有特别的顺序

  • 有不同的加密和解密类。这将解决您对使用内容的疑问。因此密码基类成为将字符串转换为其他字符串的基类。 (不是模式专家,但我相信这是Command Pattern
  • 使用对象来表示算法的好处是它可以具有状态。如果对象的创建很昂贵,您可能希望添加一个reset()方法,以便能够在新执行时重用该对象。
  • 您可以使用抽象运算符()使基类成为函数对象。此operator()在每个特定的加密和解密类中实现。使用它可以让你将这些类作为函数处理(缺点是它可能不太清楚你正在做什么)。
  • 通过指向基类(或引用或智能指针)
  • 的指针处理所有内容是正确的
  • 为了创建正确的操作类型,请设置Factory类(这也是一种模式)。这可以是具有创建者方法的类,您可以在其中指明算法和加密/解密方向。 Factory返回一个指向基类的指针,指向相应的实现。
  • 实现可以是向量或映射,也可以是某些特定工厂对象的数组(其作用是实例化不同类型的算法对象)...另外,您可以在每个派生类和存储上使用静态方法指向结构中方法的指针。
  • 结构(vector / map / array / whatever)用于快速选择正确的算法。如果algorighms的数量很小,那么使用switch语句可能就好了。该结构包含在Factory类中,并在其构造函数上初始化。
  • 您必须注意所创建对象的生命周期。对象是由工厂创建的,但谁应该销毁它们?
  • 考虑您将使用什么来表示加密/解密的邮件,以及它们变得不可打印或者它们可能变得太大。

这里有许多设计决策,许多权衡取决于不同的事情。

希望以上几行能给你一些启发。


编辑:添加更具体的示例

我们将从Operation课开始。这假设我们可以使用相同的API调用加密器和解密器

class Operation {
public:
    virtual ~Operation() { }
    virtual std::string operator()(const std::string &input)=0;

    virtual void reset() { }
};

注意事项:

  • 假设API是字符串输入,提供字符串输出。这是operator()纯虚方法。
  • 添加了虚拟析构函数。我们将主要处理对Operation的引用。但是算法的实现我需要破坏它们自己的东西,所以析构函数必须是虚拟的,这样当删除一个Operation指针时,它也会调用派生类的析构函数。
  • 添加了reset()方法。这有一个什么都不做的默认实现。可能派生的类可能存储状态,此方法旨在将操作返回到其初始步骤,这样您就不必废弃它并创建另一个。

现在有些派生类:

class MyEncoder: public Operation {
public:
    static Operation *create() {
        return new MyEncoder();
    }
    std::string operator()(const std::string &input) {
        // Do things.
        return std::string();
    }
};

class MyDecoder: public Operation {  ...  };

class OtherEncoder: public Operation { ... };

class OtherDecoder: public Operation { ... };

我只是完整展示MyEncoder我们会看到一个静态方法create,我们将在稍后讨论。 该算法的实现发生在operator()的实现上 你可以:

  • 将状态保存在MyEncoder
  • 的属性中
  • 在构造函数
  • 上初始化东西
  • ......也许会破坏析构函数中的东西。
  • 可能包含reset()方法的实现,以便在另一个调用中重用该对象。

现在为工厂:

class OperationFactory {
public:
    enum OperationDirection {
        OD_DECODER=0,
        OD_ENCODER
    };

    enum OperationType {
        OT_MY=0,
        OT_OTHER
    };
    ....
};

刚刚声明了这个类和一些枚举,以帮助我们区分编码器和解码器以及我要使用的两个算法。

我们需要一些地方来存储东西,因此Factory类以:

结束
class OperationFactory {
public:
...
private:
    typedef Operation *(*Creator)();
    typedef std::map<OperationType,Creator> OperationMap;

    OperationMap mEncoders;
    OperationMap mDecoders;
};

下面:

  • 第一个typedef为函数指针指定名称。这是一个不带参数的函数,并返回指向Operation的指针。静态方法与函数相同(至少关于函数指针)...所以这个typedef允许我们为上面的神秘create()静态方法命名。
  • 第二个typedef只是冗长的std :: map定义的快捷方式。这是从OperatonTypeCreator函数的地图。
  • 我们定义了两个映射,一个用于编码器,一个用于解码器。你可以设计一种不同的方式。

有了这个,我们可以为用户提供一些方法来获得它想要的东西:

class OperationFactory {
public:
...

    Operation *getOperation(OperationDirection _direction,OperationType _type) const {
        switch(_direction) {
        case OD_DECODER:
            return getDecoder(_type);
        case OD_ENCODER:
            return getEncoder(_type);
        default:
            // Or perhaps throw an exception
            return 0;
        }
    }

    Operation *getEncoder(OperationType _type) const {
        OperationMap::const_iterator it=mEncoders.find(_type);
        if(it!=mEncoders.end()) {
            Creator creator=it->second;
            return (*creator)();
        } else {
            // Or perhaps throw an exception
            return 0;
        }
    }

    Operation *getDecoder(OperationType _type) const {
 .... // similar but over the mDecoders
    }
....
};

因此,我们在map中查找OperationType并获取指向函数(Creator)类型的指针,我们可以调用此函数(*creator)()来获取Operation的实例我们要求的。

(*creator)()上的一些字词:

  • creator的类型为Creator ...所以它是指向函数的指针。
  • (*creator)是函数(与pint *相同,*p类型为int)...
  • (*creator)()是函数的调用。

要完成此操作,我们需要在地图中确实有一些内容......所以我们在构造函数中添加它:

class OperationFactory {
public:
....    
    OperationFactory() {
        mEncoders[OT_MY]=&MyEncoder::create;
        mEncoders[OT_MY]=&MyDecoder::create;
        mEncoders[OT_OTHER]=&OtherEncoder::create;
        mEncoders[OT_OTHER]=&OtherDecoder::create;
    }
....
};

我们为每个算法插入指向其create静态方法的指针。

最后我们如何使用它?

int main(int argc,char **argv) {
    OperationFactory f;

    Operation *o=f.getOperation(OperationFactory::OD_DECODER,OperationFactory::OT_MY);
    std::string toTransform="Hello world";
    std::string transformed=(*o)(toTransform);

    delete o; // don't forget to delete it.

}

我们在这里有一个OperationFactory f的实例,我们可以使用getOperation()方法请求创建所需的操作。

我们获得的对象可用于执行算法。请注意,(*o)(toTransform)与上面我们对创建者的调用形成类似,但存在差异:

  • o是指向Operation类型对象的指针(实际上它实际上是指针MyEncoder
  • (*o) is an object of type {操作{1}} MyEnconder`)
  • (well, really of type(*o)(toTransform)类型的operator()方法的调用。

我们可以在Creator上使用这种技术:使用对象函数而不是指向函数的指针......但是它会有更多的代码。

请注意,工厂分配内存......并且必须在不再需要时处理此内存。不这样做的方法是使用unique_ptr或shared_ptr ......

请注意,MyEncoder在找不到请求的算法时可能会返回空指针...因此调用代码应检查该可能性。

或者getOperation()的实现可能选择在找不到算法时抛出异常...再次调用代码应该有一个try / catch。


现在,如何添加新算法:

  1. 从Operation
  2. 中派生并实现您的编码器和解码器类
  3. 展开枚举OperationType
  4. 在OperationFactory构造函数中注册创建者。
  5. ......使用它。