笔记:代码重构实践之一

当前疫情严重,居家不得外出,只好将以前写的代码拿出来重构一下。首先申明,没有什么技术含量,只是作业笔记,如有幸得高人指点,当然更好。
在这里插入图片描述
这是在android上实现的莫尔斯编码器app,程序的构造不甚理想,重构之前java行数为1115。实现原理请参照在安卓手机上实现莫尔斯编码器

首先考虑过度设计,当初读了一些模式设计的书,似懂非懂用在程序中,有些地方难免画蛇添足。

abstract class AbstractMorsePlayFactory {
    //createMorsePlay的参数必须是IMorsePlay的实现类
    public abstract <T extends IMorsePlay> T createMorsePlay(Class<T> c);
}

interface IMorsePlay<T> {
    void setSpeed(T _speedFlag);
    void playDa();
    void playDi();
    void playBlank();
}

class MorsePlayFactory extends AbstractMorsePlayFactory {
    public <T extends IMorsePlay> T createMorsePlay(Class<T> c){
        //定义一个生产对象MorseCode
        IMorsePlay morsePlay = null;
        try {
            morsePlay = (T)Class.forName(c.getName()).newInstance();
        } catch(Exception e) {
            System.out.println("Create MorseCode error");
        }
        return (T)morsePlay;
    }
}
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
//...
		private IMorsePlay<Boolean> morsePlay;                            //播放类
		//实例化播放工厂类
        AbstractMorsePlayFactory morsePlayFactory = new MorsePlayFactory();
        //获得播放类的实例
        morsePlay = morsePlayFactory.createMorsePlay(MorsePlay.class);      
        
        //播放按钮的监听器
        btnPlay.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                //设定播放速度
                morsePlay.setSpeed(toggleBtnSpeed.isChecked());
                //根据txtView的内容逐个翻译成音频信号
                for (char chr : textViewMorse.getText().toString().toCharArray()){
                    switch (chr) {
                        case '.':
                            morsePlay.playDi();
                            break;
                        case '_':
                            morsePlay.playDa();
                            break;
                        default:
                            morsePlay.playBlank();
                            break;
                    }
                }
            }
        });
}

class MorsePlay implements IMorsePlay<Boolean> {
//实现IMorsePlay
}

这里用到了工厂方法模式(Factory Method Pattern),工厂方法定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到它的子类。工厂方法具有良好的封装性和扩展性,适合团体编程、分工开发,但是增加了代码量和复杂度。这里只需要一个工厂类,它的父类AbstractMorsePlayFactory不是必须的,为了简化代码,可以使用静态工厂模式(Static Factory Method),删除AbstractMorsePlayFactory,把createMorsePlay方法设为静态方法,修改如下:

//class MorsePlayFactory extends AbstractMorsePlayFactory {
class MorsePlayFactory {
	//public <T extends IMorsePlay> T createMorsePlay(Class<T> c){
    static <T extends IMorsePlay> T createMorsePlay(){
	//...
}

public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
//...
		private IMorsePlay<Boolean> morsePlay;                            //播放类
		//实例化播放工厂类
        //AbstractMorsePlayFactory morsePlayFactory = new MorsePlayFactory();
        //获得播放类的实例
        //morsePlay = morsePlayFactory.createMorsePlay(MorsePlay.class);
        morsePlay = MorsePlayFactory.createMorsePlay(MorsePlay.class);
//...
}        

下面这段代码有不少重复处理,应该有精简的余地。

class ArrayMapForCodingFactory {
    private static String[] charArray;         //字符数组 包含字母、数字、符号
    private static String[] morseCodeArray;   //字符数组对应的莫尔斯码数组
    private static String[] figuresArray;     //数字数组
    private static String[] morseLongCodeArray;    //数字数组对应的莫尔斯长码数组
    private static String[] morseShortCodeArray;    //数字数组对应的莫尔斯短码数组

    //构造函数
    ArrayMapForCodingFactory(){
        //获取字符数组和莫尔斯码数组
        Context context = MyApplication.getContextObject();
        charArray = context.getResources().getStringArray(R.array.characters);
        morseCodeArray = context.getResources().getStringArray(R.array.morse_code);
        figuresArray = context.getResources().getStringArray(R.array.figures);
        morseLongCodeArray = context.getResources().getStringArray(R.array.morse_long_code);
        morseShortCodeArray = context.getResources().getStringArray(R.array.morse_short_code);
    }

    //生产标准编码类的工厂方法
    IArrayMapForCoding createArrayMapCodingNormal(){
        //定义一个工厂生产的ArrayMapForCoding类
        IArrayMapForCoding arrayMapForCoding = null;
        try {
            //生产ArrayMapForCoding类
            arrayMapForCoding = new ArrayMapForCoding(charArray, morseCodeArray);
        } catch (Exception e){
            System.out.println("ArrayMapCodingNormal类生成错误!");
        }
        return arrayMapForCoding;
    }

    //生产长码电报编码类的工厂方法
    IArrayMapForCoding createArrayMapCodingLong(){
        //定义一个工厂生产的ArrayMapForCoding类
        IArrayMapForCoding arrayMapForCoding = null;
        try {
            //生产ArrayMapForCoding类
            arrayMapForCoding = new ArrayMapForCoding(figuresArray, morseLongCodeArray);
        } catch (Exception e){
            System.out.println("ArrayMapCodingLong类生成错误!");
        }
        return arrayMapForCoding;
    }

    //生产短码电报编码类的工厂方法
    IArrayMapForCoding createArrayMapCodingShort(){
        //定义一个工厂生产的ArrayMapForCoding类
        IArrayMapForCoding arrayMapForCoding = null;
        try {
            //生产ArrayMapForCoding类
            arrayMapForCoding = new ArrayMapForCoding(figuresArray, morseShortCodeArray);
        } catch (Exception e){
            System.out.println("ArrayMapCodingShort类生成错误!");
        }
        return arrayMapForCoding;
    }
}

删除不必要的异常处理,直接调用ArrayMapForCoding的构造函数,获得摩尔斯编码类。

class ArrayMapForCodingFactory {
//...
    //生产标准编码类的工厂方法
    IArrayMapForCoding createArrayMapCodingNormal(){
        return new ArrayMapForCoding(charArray, morseCodeArray);
    }
    //生产长码电报编码类的工厂方法
    IArrayMapForCoding createArrayMapCodingLong(){
        return new ArrayMapForCoding(figuresArray, morseLongCodeArray);
    }
    //生产短码电报编码类的工厂方法
    IArrayMapForCoding createArrayMapCodingShort(){
        return new ArrayMapForCoding(figuresArray, morseShortCodeArray);
    }

很显然,下面这段代码也可以精简。

class AllCapTransformationMethod extends ReplacementTransformationMethod {
    @Override
    protected char[] getOriginal() {
        char[] aa;
        aa = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
        return aa;
    }
    @Override
    protected char[] getReplacement() {
        char[] cc;
        cc = new char[]{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
        return cc;
    }
}

修改后是这样的。

class AllCapTransformationMethod extends ReplacementTransformationMethod {
    @Override
    protected char[] getOriginal() {
        return new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
    }

    @Override
    protected char[] getReplacement() {
        return new char[]{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
    }
}

下面这个类也需要提炼。

class MorsePlay implements IMorsePlay<Boolean> {
//...
    public MorsePlay() {
        // 设置最多可容纳4个音频流,音频的品质为5
        // load方法加载指定音频文件,并返回所加载的音频ID。
        Context context = MyApplication.getContextObject();
        streamIdSlowDi = soundPool.load(context, R.raw.morse_di_070ms_600hz , 1);
        streamIdSlowDa = soundPool.load(context, R.raw.morse_da_210ms_600hz , 1);
        streamIdFastDi = soundPool.load(context, R.raw.morse_di_050ms_600hz , 1);
        streamIdFastDa = soundPool.load(context, R.raw.morse_da_150ms_600hz , 1);
    }

    public void finalize(){
        soundPool.release();
        try {
            super.finalize();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    public void setSpeed(Boolean _speedFlag){
        speedFlag = _speedFlag;
    }

    public void playDa(){
        if (speedFlag){
            soundPool.play(streamIdFastDa,1.0f, 1.0f, 0, 0, 1.0f);
            delay(delayFastDa + delayFastDi);
        }
        else{
            soundPool.play(streamIdSlowDa,1.0f, 1.0f, 0, 0, 1.0f);
            delay(delaySlowDa + delaySlowDi);
        }
    }

    public void playDi(){
        if (speedFlag){
            soundPool.play(streamIdFastDi,1.0f, 1.0f, 0, 0, 1.0f);
            delay(delayFastDi + delayFastDi);
        }
        else{
            soundPool.play(streamIdSlowDi,1.0f, 1.0f, 0, 0, 1.0f);
            delay(delaySlowDi + delaySlowDi);
        }
    }

    public void playBlank(){
        if (speedFlag){
            delay(delayFastDa + delayFastDi);
        }
        else{
            delay(delaySlowDa + delaySlowDi);
        }
    }
    private void delay(long _ms){
        try {
            Thread.sleep(_ms);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

提炼后精简代码。

class MorsePlay implements IMorsePlay<Boolean> {
//...
    public MorsePlay() {
        // 设置最多可容纳4个音频流,音频的品质为5
        // load方法加载指定音频文件,并返回所加载的音频ID。
        Context context = MyApplication.getContextObject();
        streamIdSlowDi = soundPool.load(context, R.raw.morse_di_070ms_600hz , 1);
        streamIdSlowDa = soundPool.load(context, R.raw.morse_da_210ms_600hz , 1);
        streamIdFastDi = soundPool.load(context, R.raw.morse_di_050ms_600hz , 1);
        streamIdFastDa = soundPool.load(context, R.raw.morse_da_150ms_600hz , 1);
    }

    public void finalize(){
        soundPool.release();
        try {
            super.finalize();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    public void setSpeed(Boolean _isFast){
        isFast = _isFast;
    }

    public void playDa(){
        soundPool.play(isFast ? streamIdFastDa : streamIdSlowDa,1.0f, 1.0f, 0, 0, 1.0f);
        delay(isFast ? delayFastDa + delayFastDi : delaySlowDa + delaySlowDi);
    }

    public void playDi(){
        soundPool.play(isFast ? streamIdFastDi : streamIdSlowDi,1.0f, 1.0f, 0, 0, 1.0f);
        delay(isFast ? delayFastDi + delayFastDi : delaySlowDi + delaySlowDi);
    }

    public void playBlank(){
        delay(isFast ? delayFastDa + delayFastDi : delaySlowDa + delaySlowDi);
    }
    private void delay(long _ms){
        try {
            Thread.sleep(_ms);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

下面这两个类使用String进行字符串拼接,显然应该改正,否则无端消耗内存空间。另外使用了MainActivity中的类,破坏了封装性。

class NormalCoder implements ICoder {
    //获得莫尔斯码
    public String code(String _text) {
        String returnValue = "";
        //把字母、数字和空格翻译成莫尔斯码 数字用长码表示
        for (char chr : _text.toUpperCase().toCharArray()){
            if (chr == ' '){
                returnValue += " ";
            }
            else {
                returnValue += MainActivity.arrayMapForCodingNormal.GetMorseCode(chr);
            }
            returnValue += " ";
        }
        return returnValue;
    }
}

class TelegramCoder implements ICoder {
    //数字长码标识
    private final boolean IsLongCode;

    //构造函数 指定长短码
    TelegramCoder(boolean _isLongCode){ IsLongCode = _isLongCode; }

    //获得莫尔斯码
    //把数字和空格翻译成莫尔斯码 数字表示取决于长码标识IsLongCode
    public String code(String _text){
        String returnValue = "";
        for (char chr : _text.toUpperCase().toCharArray()){
            if (chr == ' '){
                returnValue += " ";
            }
            else {
                if (IsLongCode){
                    returnValue += MainActivity.arrayMapForCodingLong.GetMorseCode(chr);
                }
                else {
                    returnValue += MainActivity.arrayMapForCodingShort.GetMorseCode(chr);
                }
            }
            returnValue += " ";
        }
        return returnValue;
    }
}

应该使用StringBuilder,并且用构造函数从外部注入arrayMapForCodingNormal类,两个类合并为一个,合并后新类代码如下。

public class MorseCoder implements ICoder {
    private IArrayMapForCoding arrayMapForCoding;
    //构造函数,注入ArrayMapForCoding

    MorseCoder(IArrayMapForCoding _arrayMapForCoding) {
        arrayMapForCoding = _arrayMapForCoding;
    }

    //获得莫尔斯码
    @Override
    public String code(String _text) {
        StringBuilder stringBuilder = new StringBuilder();
        //把字母、数字和空格翻译成莫尔斯码
        for (char chr : _text.toUpperCase().toCharArray()) {
            if (chr == ' ') {
                stringBuilder.append(" ");
            } else {
                stringBuilder.append(arrayMapForCoding.GetMorseCode(chr));
            }
            stringBuilder.append(" ");
        }
        return stringBuilder.toString();
    }
}

修改他们的使用者MainActivity,删除原有的两个类。

public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
//...
	protected void onCreate(Bundle savedInstanceState) {
	//...
        //莫尔斯码生成按钮的监听器
        btnCode.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                boolean isLongCode = toggleBtnLong.isChecked();   //指定长短码
                CoderContext coderContext;            //使用策略模式,封装角色 简化顶层逻辑
                //根据电文格式和长短码,实例化编码类
                if (toggleBtnFormat.isChecked()){
                	//coderContext = new CoderContext(new TelegramCoder(isLongCode));
                    coderContext = new CoderContext(new MorseCoder(isLongCode ? arrayMapForCodingLong : arrayMapForCodingShort));
                }
                else {
                	//coderContext = new CoderContext(new NormalCoder());
                    coderContext = new CoderContext(new MorseCoder(arrayMapForCodingNormal));
                }
                textViewMorse.setText(coderContext.code(editText.getText().toString()));
            }
        });
	}
}

下面的类用于在子类中直接获取资源,把context定义为静态变量,存在内存泄漏风险,导致Activity无法正常销毁。

public class MyApplication extends Application {
    private static Context context;
    @Override
    public void onCreate() {
        super.onCreate();
        //获取Context
        context = getApplicationContext();
    }
    //返回
    public static Context getContextObject(){
        return context;
    }
}

修改方法是删除MyApplication类,把android资源直接传给需要使用资源的子类。

public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
//...
	protected void onCreate(Bundle savedInstanceState) {
	//...
        //实例化编码工厂类
        //ArrayMapForCodingFactory arrayMapForCodingFactory = new ArrayMapForCodingFactory();
        ArrayMapForCodingFactory arrayMapForCodingFactory = new ArrayMapForCodingFactory(res);
        //获得播放类的实例
        //morsePlay = MorsePlayFactory.createMorsePlay();
        morsePlay = MorsePlayFactory.createMorsePlay(this);
        //获得制作音频文件类的实例
        //makeMorseAudioFile = new MakeMorseAudioFile();
        makeMorseAudioFile = new MakeMorseAudioFile(res);
	}
}

子类的修改不再赘述,需要说明的是MorsePlay类的实例是在工厂方法中用类反射取得的,原来是无参类反射,修改为有参类反射,如下代码。

class MorsePlayFactory {
	//static <T extends IMorsePlay> T createMorsePlay(){
    static <T extends IMorsePlay> T createMorsePlay(Context _context){
        //定义一个生产对象MorseCode
        IMorsePlay morsePlay = null;
        try {
        	//morsePlay = (T)Class.forName(MorsePlay.class.getName()).newInstance();
            morsePlay = (T)Class.forName(MorsePlay.class.getName()).getDeclaredConstructor(Context.class).newInstance(_context);
        } catch(Exception e) {
            System.out.println("Create MorsePlay error");
        }
        return (T)morsePlay;
    }
}

经过测试,代码正确运行,至此java代码行数为982,减少了133行。修改后的代码放在老地方

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值