HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Caesar Cipher MVC implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
implementationcaesarmvccipher

Problem

I implemented the Caesar Cipher in Java, and I wanted to practice MVC.

This is my model:

package biz.tugay.caesarcipher;

import java.util.Locale;

/*
    Encyrpts a clear text using Caeser Cipher (https://en.wikipedia.org/wiki/Caesar_cipher)
    with given shift amount.

    Provided shift amount (i.e. key) must be a positive integer less than 26.
    Only English alphabet is supported and encyrpted text will be in uppercase.

    Shift amount 0 will return the same clear text.
 */
public final class CaesarCipher {

    private String clearText;
    private int key;

    public String encryptText() {
        final StringBuilder cipherTextBuilder = new StringBuilder();

        final String clearTextUpperCase = clearText.toUpperCase(Locale.US);
        final char[] clearTextUpperCaseCharArray = clearTextUpperCase.toCharArray();

        for (final char c : clearTextUpperCaseCharArray) {
            if (c  90) { // If the character is not between A .. Z, append white space.
                cipherTextBuilder.append(" ");
                continue;
            }
            final Character encryptedCharacter = encryptCharacter(c);
            cipherTextBuilder.append(encryptedCharacter);
        }

        return cipherTextBuilder.toString();
    }

    private Character encryptCharacter(final char c) {
        final int initialShift = c + key;
        final int finalShift;

        if (initialShift > 90) {
            // This is the case where we go beyond Z, we must cycle back to A.
            finalShift = (initialShift % 90) + 64;
        } else {
            // We are in the boundries so no need to cycle..
            finalShift = initialShift;
        }

        return (char) finalShift;
    }

    public void setClearText(String clearText) {
        this.clearText = clearText;
    }

    public void setKey(int key) {
        this.key = key;
    }
}


My View:

```
package biz.tugay;

import java.util.Scanner;

public class CaesarCipherView {

public

Solution

Your understanding of MVC seems perfectly fine. You have the business logic in the model. The user input in the view. And lastly a controller to couple the user input to the model and back.

The only problem I have with your program is that it isn't interesting enough. Using MVC here feels too much like you're over-designing things.

What if we had just 2 classes. The CaesarCipher class for the encryption with only this method:

public static String encryptText(String plainText, int key) {...}


And your Viewer class but with one added method that does this:

while(true){
    String text = askForClearText();
    int key = askForKey();
    presentEncrpytedText(CaesarCipher.encryptText(text,key));
}


This still looks pretty decent to me ...

It becomes more interesting if we add some behaviour. Let's write a program that does the following things:

The user can choose between 2 things:

1) change the cypher

2) choose to either encode/decode a piece of text with the current cypher.

Now I would slightly change your CaesarCipher class. Let's remove the text from the state and just pass it in as a parameter for the encrypt method and for a new decrypt method.

public class CeasarCypher {
    private int key = 0;

    public String encryptText(String clearText) {
        //encrypt the given text
    }

    public String decryptText(String encryptedText){
        //decrypt the given text
    }

    public void setKey(int key){
        this.key = key;
    }
}


Now we need to make a choice about who initiates anything in your program. Either it's the controller that asks the view for user input. This is what you did.

Or have the view decide what the user has input and then tell this to the controller so it can handle things.

Since we're looking at the overall design, let's first guess a little bit what we want to change in the next versions of the program. One thing I would want to do is later on provide a graphical interface for the user. I would image a single screen that has a textbox to input a text. And 3 buttons for the user choices. A button to change the key (which might popup a new input screen to enter the cypher), a button to encrypt the given text and a last button to decrypt the given tekst.

In this scenario it makes a lot more sense that the view initiates everything. The moment a button is pressed, the view for tells the controller for example: "the user wants to decrypt some tekst".

Let's make sure that we get the full advantage of using MVC. Namely that we can swap to a different view and everything still works.

To be able to do this we need to figure out what methods the Controller has to provide for the View to initiate things. And what methods the view has to provide for the controller to provide feedback.

I would go with something like this:

public class Controller {
    View view;
    Cypher cypher;

    public Controller(View view, Cypher cypher) {
        this.view = view;
        view.setController(this);
        this.cypher = cypher;
    }

    public void changeKey() {
        cypher.setKey(view.getKey());
    }

    public void encryptText(){
        String clearText = view.getClearText();
        String encryptedText = cypher.encrypt(clearText);
        view.presentEncryptedText(encryptedText);
    }

    public void decryptText(){
        String encryptedText = view.getEncryptedText();
        String clearText = cypher.encrypt(clearText);
        view.presentEncryptedText(encryptedText);
    }
}


And the view

public abstract class View {
    public abstract void setController(Controller controller);
    public abstract void presentEncryptedText(String text);
    public abstract void presentDecryptedText(String text);
    public abstract String getEncryptedText();
    public abstract String getDecryptedText();
    public abstract int getKey();
}


Now we can provide either a textual or a graphical view.

For the textual view we can do something like this:

```
public class TextualView implements View {
private Controller controller;
Scanner scanner = new Scanner(System.in);

public void setController(Controller controller) {
this.controller = controller;
}

public void presentEncryptedText(String text) {
System.out.println("This is your encrypted text: "+text);
}

public void presentDecryptedText(String text) {
System.out.println("This is your decrypted text: "+text);
}

public void mainMenu() {
boolean userWantsToQuit = false;
while(!userWantsToQuit) {
System.out.println("What would you like to do?"){
System.out.println("0) Quit");
System.out.println("1) Change key");
System.out.println("2) Encrypt message");
System.out.println("3) Decrypt message");
int choice = scanner.nextInt();
switch(choice){
case 0 :
userWantsToQuit = true;

Code Snippets

public static String encryptText(String plainText, int key) {...}
while(true){
    String text = askForClearText();
    int key = askForKey();
    presentEncrpytedText(CaesarCipher.encryptText(text,key));
}
public class CeasarCypher {
    private int key = 0;

    public String encryptText(String clearText) {
        //encrypt the given text
    }

    public String decryptText(String encryptedText){
        //decrypt the given text
    }

    public void setKey(int key){
        this.key = key;
    }
}
public class Controller {
    View view;
    Cypher cypher;

    public Controller(View view, Cypher cypher) {
        this.view = view;
        view.setController(this);
        this.cypher = cypher;
    }

    public void changeKey() {
        cypher.setKey(view.getKey());
    }

    public void encryptText(){
        String clearText = view.getClearText();
        String encryptedText = cypher.encrypt(clearText);
        view.presentEncryptedText(encryptedText);
    }

    public void decryptText(){
        String encryptedText = view.getEncryptedText();
        String clearText = cypher.encrypt(clearText);
        view.presentEncryptedText(encryptedText);
    }
}
public abstract class View {
    public abstract void setController(Controller controller);
    public abstract void presentEncryptedText(String text);
    public abstract void presentDecryptedText(String text);
    public abstract String getEncryptedText();
    public abstract String getDecryptedText();
    public abstract int getKey();
}

Context

StackExchange Code Review Q#158154, answer score: 5

Revisions (0)

No revisions yet.