如何使用构造函数参数变化的工厂模式

I'm writing a package where I need to have PHP objects represent MySQL data types.

For example, I have classes for IntType, VarCharType, BlobType, etc.

Each of these have different constructor arguments. I.e. IntType has a signed property, VarChar has collation, etc.

The source for which of these types I need to construct from from the database. So I may have "INT(10) UNSIGNED", "VARCHAR(255)", and "BLOB".

So currently I have a method that examine and pull apart these strings, and it uses a lot of IF and SWITCH statements to determine which class needs to be constructed.

I know this is ugly but I'm not sure on the best way around it. All textbook examples of the Factory pattern are based on producing classes that have identical constructor arguments.

Currently I have two ideas to solve this:

  • Use something like the Chain Of Responsibility pattern, where I have a factory per data type, and each will decide if it can handle the data type string, and if it can't it will be passed onto the next factory.
  • Have a factory per data type, and a super factory that would examine the data type string to determine the correct data type factory to handle the string.

Because I have not see much theory on this, I'm a little unsure of the most ideal direction.

The following is the offending code to show exactly how ugly it is...

<?php

class Factory
{
    // ...

    /**
     * @param string $dataTypeString Data type string as reported by MySQL when executing SHOW FULL COLUMNS FROM table;
     * @param null $collation
     */
    public function createDataType($dataTypeString, $collation = null)
    {
        $dataType = null;

        if (preg_match('/^(tiny|small|medium|big)?int(eger)?\((\d+)\)( unsigned)?/i', $dataTypeString, $matches)) {
            $displayWidth = $matches[3];
            $signed = empty($matches[4]);

            switch (strtolower($matches[1])) {
                case 'tiny':
                    $dataType = new TinyIntType($displayWidth, $signed);
                    break;
                case 'small':
                    $dataType = new SmallIntType($displayWidth, $signed);
                    break;
                case 'medium':
                    $dataType = new MediumIntType($displayWidth, $signed);
                    break;
                case 'big':
                    $dataType = new BigIntType($displayWidth, $signed);
                    break;
                default:
                    $dataType = new IntType($displayWidth, $signed);
                    break;
            }
        } elseif (preg_match('/^(decimal|float|double)\((\d+),(\d+)\)( unsigned)?/i', $dataTypeString, $matches)) {
            $precision = $matches[2];
            $scale = $matches[3];
            $signed = empty($matches[4]);

            switch (strtolower($matches[1])) {
                case 'decimal':
                    $dataType = new DecimalType($precision, $scale, $signed);
                    break;
                case 'double':
                    $dataType = new DoubleType($precision, $scale, $signed);
                    break;
                case 'float':
                    $dataType = new FloatType($precision, $scale, $signed);
            }
        } elseif (preg_match('/^(var)?binary\((\d+)\)$/i', $dataTypeString, $matches)) {
            $isVarBinary = !empty($matches[1]);
            $length = $matches[2];

            if (strtolower($isVarBinary)) {
                $dataType = new VarBinaryType($length);
            } else {
                $dataType = new BinaryType($length);
            }
        } elseif (preg_match('/^bit\((\d+)\)$/i', $dataTypeString, $matches)) {
            $dataType = new BitType($matches[1]);
        } else {
            $characterSet = !empty($collation) ? $this->getCharacterSet($collation) : null;

            if (preg_match('/^(var)?char\((\d+)\)$/i', $dataTypeString, $matches)) {
                $isVarChar = !empty($matches[1]);
                $length = $matches[2];

                if ($isVarChar) {
                    $dataType = new VarCharType($length, $characterSet, $collation);
                } else {
                    $dataType = new CharType($length, $characterSet, $collation);
                }
            } elseif (preg_match("/(set|enum)(\('.+'\))/i", $dataTypeString, $matches)) {
                $options = $this->parseOptions($matches[2]);

                switch (strtolower($matches[1])) {
                    case 'set':
                        $dataType = new SetType($options, $characterSet, $collation);
                        break;
                    case 'enum':
                        $dataType = new EnumType($options, $characterSet, $collation);
                        break;
                }
            } else {
                switch (strtolower($dataTypeString)) {
                    case 'text':
                        $dataType = new TextType($characterSet, $collation);
                        break;
                    case 'tinytext':
                        $dataType = new TinyTextType($characterSet, $collation);
                        break;
                    case 'mediumtext':
                        $dataType = new MediumTextType($characterSet, $collation);
                        break;
                    case 'longtext':
                        $dataType = new LongTextType($characterSet, $collation);
                        break;
                    case 'blob':
                        $dataType = new BlobType();
                        break;
                    case 'tinyblob':
                        $dataType = new TinyBlobType();
                        break;
                    case 'mediumblob':
                        $dataType = new MediumBlobType();
                        break;
                    case 'longblob':
                        $dataType = new LongBlobType();
                        break;
                }
            }
        }

        return $dataType;
    }
}

Update: This has been reposted at https://codereview.stackexchange.com/questions/134709/a-pattern-to-clean-up-this-factory-method